case
case — это управляющее выражение, которое работает подобно сопоставлению с образцом. Оно позволяет записывать цепочки if-else-if с небольшим изменением семантики и использовать некоторые более мощные конструкции.
В базовой форме оно позволяет сопоставить значение с другими значениями:
case exp
when value1, value2
do_something
when value3
do_something_else
else
do_another_thing
end
# The above is the same as:
tmp = exp
if value1 === tmp || value2 === tmp
do_something
elsif value3 === tmp
do_something_else
else
do_another_thing
end
Для сравнения выражения со значением case используется оператор проверки равенства case ===. Он определён как метод в Object и может быть переопределён подклассами, чтобы обеспечить осмысленную семантику в инструкциях case. Например, Class определяет проверку равенства case так, что она возвращает истину, если объект является экземпляром этого класса; Regex — если значение соответствует регулярному выражению; а Range — если значение входит в этот диапазон.
Если выражение when имеет тип, используется is_a?. Кроме того, если выражение case является переменной или присваиванием переменной, тип переменной ограничивается:
case var
when String
# var : String
do_something
when Int32
# var : Int32
do_something_else
else
# here var is neither a String nor an Int32
do_another_thing
end
# The above is the same as:
if var.is_a?(String)
do_something
elsif var.is_a?(Int32)
do_something_else
else
do_another_thing
end
Вызвать метод у выражения case в when можно с помощью синтаксиса неявного объекта:
case num
when .even?
do_something
when .odd?
do_something_else
end
# The above is the same as:
tmp = num
if tmp.even?
do_something
elsif tmp.odd?
do_something_else
end
После условия when можно использовать then, чтобы записать тело в одну строку.
case exp
when value1, value2 then do_something
when value3 then do_something_else
else do_another_thing
end
Наконец, можно опустить значение case:
case
when cond1, cond2
do_something
when cond3
do_something_else
end
# The above is the same as:
if cond1 || cond2
do_something
elsif cond3
do_something_else
end
Иногда так код выглядит естественнее при чтении.
Литерал кортежа
Если выражение case является литералом кортежа, возникают некоторые семантические отличия, когда условие when также является литералом кортежа.
Размеры кортежей должны совпадать
case {value1, value2}
when {0, 0} # OK, 2 elements
# ...
when {1, 2, 3} # Syntax error: wrong number of tuple elements (given 3, expected 2)
# ...
end
Подчёркивание допускается
case {value1, value2}
when {0, _}
# Matches if 0 === value1, no test done against value2
when {_, 0}
# Matches if 0 === value2, no test done against value1
end
Неявный объект допускается
case {value1, value2}
when {.even?, .odd?}
# Matches if value1.even? && value2.odd?
end
Сравнение с типом выполняет проверку is_a?
case {value1, value2}
when {String, Int32}
# Matches if value1.is_a?(String) && value2.is_a?(Int32)
# The type of value1 is known to be a String by the compiler,
# and the type of value2 is known to be an Int32
end
Исчерпывающий case
Использование in вместо when создаёт исчерпывающее выражение case; в исчерпывающем case пропуск любого из обязательных условий in приводит к ошибке компиляции. Исчерпывающий case не может содержать ни одного предложения when или else.
Компилятор поддерживает следующие условия in:
Проверки объединённого типа
Если выражение case имеет значение объединённого типа, в качестве условия можно использовать каждый из типов объединения:
# var : (Bool | Char | String)?
case var
in String
# var : String
in Char
# var : Char
in Bool
# var : Bool
in nil # or Nil, but .nil? is not allowed
# var : Nil
end
Логические значения
Если выражение case имеет значение типа Bool, в качестве условий можно использовать литералы true и false:
# var : Bool
case var
in true
do_something
in false
do_something_else
end
Значения перечисления
Если выражение case имеет значение перечисления без флагов, его элементы можно использовать в качестве условий — как константы или методы-предикаты.
enum Foo
X
Y
Z
end
# var : Foo
case var
in Foo::X
# var == Foo::X
in .y?
# var == Foo::Y
in .z? # :z is not allowed
# var == Foo::Z
end
Литералы кортежей
Условия должны охватывать все возможные сочетания элементов выражения case:
# value1, value2 : Bool
case {value1, value2}
in {true, _}
# value1 is true, value2 can be true or false
do_something
in {_, false}
# here value1 is false, and value2 is also false
do_something_else
end
# Error: case is not exhaustive.
#
# Missing cases:
# - {false, true}
To the extent possible under law, the persons who contributed to this workhave waived
all copyright and related or neighboring rights to this workby associating CC0 with it.
https://crystal-lang.org/reference/1.2/syntax_and_semantics/case.html