четверг, 20 января 2011 г.

Groovy switch magic

Рассмотрим такой простой и тривиальный оператор, как switch. Казалось бы, ну что можно придумать нового вокруг него? Ан-нет, можно.


В Java как известно, switch-ить можно только по целым числам и по Enum-ам, и все это жестко зашито в синтаксисе языка. Остальное - запрещено. В Groovy же этот оператор работает следующим образом.


Общая логика работы оператора switch проста и знакома каждому программисту:

switch (candidate) {
  case classifier1 : handle1() ; break
  case classifier2 : handle2() ; break
  default : handleDefault()
}
А теперь надо вспомнить, что в Groovy, в отличие от Java, "все есть объект". Т.е. абсолютно все, без исключений в виде примитивных типов. Соответственно, classifier1, classifier2 и candidate - объекты. И для того, чтобы вышеприведенная конструкция работала, необходимо и достаточно, чтобы classifier 1 и classifier2 имели метод isCase(candidate), возвращающий булево значение. Все просто.
И еще несколько ценных свойств свитчей в Groovy. 
Во-первых, классификаторы classifier1 и classifier2 вовсе не обязаны быть одного и того же типа. Во-вторых, выражение-candidate может удовлетворять нескольким case-ам, а не одному. Откуда следует, что порядок case-ов в Groovy, в отличие от Java,  важен.


Ну и небольшой пример, что можно делать в switch-ах в Groovy:

switch (20) {
   case 2 : assert false ; break // Равно двум?
   case 2..15 : assert false ; break // Попадает в диапазон?
   case [11,12,14,16] : assert false ; break // Входит в список?
   case Double : assert false ; break // является Double-типом?
   case {it % 5 == 0}: assert true ; break // Делится на пять?
   case ~/../ : assert true; break // В эту ветку мы не заходим
   default : assert false ; break
}

4 комментария:

  1. На мой ФПшный взгляд "break" явно лишний.
    В Груви есть нормальное сопоставление с образцом?

    ОтветитьУдалить
  2. Что именно ты имеешь в виду (думаю не регулярные
    выражения?)? Которые, кстати, тут поддерживаются
    на уровне языка, я даже писал в этом блоге про них :)

    Такого как есть в Haskell-e - нет, нету.

    По мне, описанный выше пример - это весьма мощное сопоставление для кусков логики внутри методов.

    Кроме того, есть такая концепция, как мультиметоды.

    Вот пример:

    http://mrhaki.blogspot.com/2009/09/groovy-goodness-multimethods-or.html

    Если коротко, то в Java выбор метода (overloading)
    делается на основании статического типа
    его параметров, а в Groovy - на основании динамического типа.

    ОтветитьУдалить
  3. Конечно я имею в виду сопоставление с образцом. Мультиметоды это не совсем то, хотя тоже реализуют динамическую диспетчеризацию.

    Обнаружил что мой закрытий (в одной местечковой локалке) бложик проиндексировал Гугл, при том самое интересное. Я там для узкого круга лиц рассказывал про Немерл (читать снизу вверх):


    http://webcache.googleusercontent.com/search?q=cache:pNjJcGsmLDEJ:portal.lanpolis.ru/board/journal.php%3Fuser%3D3824%26st%3D0%26debug%3D1+nemerle+hardcase+%D1%81%D0%BE%D0%BF%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5+%D1%81+%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D1%86%D0%BE%D0%BC&cd=6&hl=ru&ct=clnk&source=www.google.com

    ОтветитьУдалить
  4. Кстати, C# 4.0 научился выполнять выбор перегрузки на основе динамического типа. Для этого нужно вызвать метод у dynamic-типа. Немерле такое тоже умеет, но для этого нужно использовать макрос late :)

    ОтветитьУдалить