Предложение case в Ruby

Во всех современных языках есть та или иная форма многопутевого ветвления. В
C/C++ и Java это предложение switch, а в Pascal - предложение case. Служат они одной и той же цели и функционируют примерно,одинаково.
Предложение case в Ruby похоже, но при ближайшем рассмотрении оказывается
настолько уникальным, что варианты ветвления, принятые в С и в Pascal,кажутся близкими родственниками. Точного аналога предложению case в Ruby нет ни в каком другом знакомом мне языке, поэтому оно заслуживает особого внимания.
Выше мы уже рассматривали синтаксис этого предложения, а теперь сосредоточимся на его семантике.
Для начала рассмотрим тривиальный пример. Выражение expression сравнивается
со значением value, и, если они совпадают, выполняется некоторое
действие. Ничего удивительного.
case expression
when value
некоторое действие
end
В Ruby для этой цели есть специальный оператор === (называется оператором
отношения). Иногда его еще называют (не совсем правильно) оператором
ветвящегося равенства. Неправильность в том, что он не всегда относится
именно к проверке на равенство.
Предыдущее предложение можно записать и так:
if value === expression
некоторое действие
end
Не путайте оператор отношения с оператором проверки на равенство (==).
Они принципиально различны, хотя во многих случаях ведут себя одинаково.
Оператор отношения определен по-разному в разных классах, а
для данного класса его поведение может зависеть от типа переданного операнда.
Не думайте, что проверяемое выражение - это объект, которому сравниваемое
значение передается в качестве параметра. На самом деле как раз наоборот
(мы это только что видели).
Это подводит нас к наблюдению, что х === у означает вовсе не то же самое,
что у === х! Иногда результат совпадает, но в общем случае оператор отношения
не коммутативен. (Именно поэтому нам не нравится термин «оператор
ветвящегося равенства» - ведь проверка на равенство всегда коммутативна.)
Если перевернуть исходный пример, окажется, что следующий код
ведет себя иначе:
case value
when expression
некоторое действие
end
В качестве примера рассмотрим строку str и образец (регулярное выражение)
pat, с которым эта строка успешно сопоставляется.
Выражение str =~ pat истинно, как в языке Perl. Поскольку Ruby определяет
противоположную семантику оператора в классе Regexp, можно
также сказать, что выражение pat =- str истинно. Следуя той же логике,
мы обнаруживаем, что истинно и pat === str (исходя из того, как определен
оператор === в классе Regexp). Однако выражение str === pat истинным не
является. А значит, фрагмент case "Hello"
when /Hell/
puts "Есть соответствие."
else
puts "Нет соответствия."
end
делает не то же самое, что фрагмент
case /Hell/
when "Hello"
puts "Есть соответствие."
else
puts "Нет соответствия."
end
Если это вас смущает, просто постарайтесь запомнить. А если не смущает,
лучше!
Программисты, привыкшие к С, могут быть озадачены отсутствием предлояа
ний break в ветвях case. Такое использование break в Ruby необязательно (и
допустимо). Связано это с тем, что «проваливание» редко бывает желателы
при многопутевом ветвлении. В конце каждой ветви when имеется неявный
реход на конец предложения case. В этом отношении Ruby напоминает Rasa
Значения в каждой ветви case могут быть произвольными. На типы никакн
ограничений не налагается. Они не обязаны быть константами; допускаютс
и переменные, и сложные выражения. Кроме того, в ветви может проверят]
ся попадание в диапазон.
В ветвях case могут находиться пустые действия (пустые предложения
Значения в разных ветвях не обязательно должны быть уникальными - д(
пускаются перекрытия, например:
case х
when О
.5
"Вторая ветвь"
.10
"Третья ветвь"
when 1
puts
when 5
puts
else
puts
end
Четвертая ветвь"
Если x принимает значение 0, ничего не делается. Для значения 5 печата
ся строка «Вторая ветвь» - несмотря на то что 5 удовлетворяет и услови
в третьей ветви.
Перекрытие ветвей допускается потому, что они вычисляются в строга
порядке и выполняется закорачивание. Иными словами, если вычисленивыражения в какой-то ветви оказалось успешным, то следующие ветви не
вычисляются. Поэтому не стоит помещать в ветви case выражения, в которых
вызываются методы с побочными эффектами. (Впрочем, такие вызовы
в любом случае сомнительны). Имейте также в виду, что такое поведение
может замаскировать ошибки, которые произошли бы во время выполнения,
если бы выражение вычислялось. Например:
case х
when 1..10
puts "Первая ветвь"
when foobar() # Возможен побочный эффект?
puts "Вторая ветвь"
when 5/0 # Деление на нуль!
puts "Третья ветвь"
else
puts "Четвертая ветвь"
end
Если x находится в диапазоне от 1 до 10, то метод f oobar () не вызывается, а выражение 5 / 0 (которое, естественно, привело бы к ошибке) не вычисляется.