• Предыдущее предложение можно записать и так:
if value === expression
некоторое действие
end
• Не путайте оператор отношения с оператором проверки на равенство (==
). Они принципиально различны, хотя во многих случаях ведут себя одинаково. Оператор отношения определен по-разному в разных классах, а для данного класса его поведение может зависеть от типа переданного операнда.
• Не думайте, что проверяемое выражение — это объект, которому сравниваемое значение передается в качестве параметра. На самом деле как раз наоборот (мы это только что видели).
• Это подводит нас к наблюдению, что x===y
означает вовсе не то же самое, что y===x
! Иногда результат совпадает, но в общем случае оператор отношения не коммутативен. (Именно поэтому нам не нравится термин «оператор ветвящегося равенства» — ведь проверка на равенство всегда коммутативна.) Если перевернуть исходный пример, окажется, что следующий код ведет себя иначе:
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 напоминает Pascal.
• Значения в каждой ветви case
могут быть произвольными. На типы никаких ограничений не налагается. Они не обязаны быть константами; допускаются и переменные, и сложные выражения. Кроме того, в ветви может проверяться попадание в диапазон.
• В ветвях case
могут находиться пустые действия (пустые предложения). Значения в разных ветвях не обязательно должны быть уникальными — допускаются перекрытия, например:
case x
when 0
when 1..5
puts "Вторая ветвь"
when 5..10
puts "Третья ветвь"
else
puts "Четвертая ветвь"
end
Если x принимает значение 0, ничего не делается. Для значения 5 печатается строка «Вторая ветвь» — несмотря на то что 5 удовлетворяет и условию в третьей ветви.
• Перекрытие ветвей допускается потому, что они вычисляются в строгом порядке и выполняется закорачивание. Иными словами, если вычисление выражения в какой-то ветви оказалось успешным, то следующие ветви не вычисляются. Поэтому не стоит помещать в ветви case
выражения, в которых вызываются методы с побочными эффектами. (Впрочем, такие вызовы в любом случае сомнительны). Имейте также в виду, что такое поведение может замаскировать ошибки, которые произошли бы во время выполнения, если бы выражение вычислялось. Например:
case x
when 1..10
puts "Первая ветвь"
when foobar() # Возможен побочный эффект?
puts "Вторая ветвь"
when 5/0 # Деление на нуль!
puts "Третья ветвь"
else
puts "Четвертая ветвь"
end
Если x находится в диапазоне от 1 до 10, то метод foobar()
не вызывается, а выражение 5/0 (которое, естественно, привело бы к ошибке) не вычисляется.
1.5.4. Рубизмы и идиомы
Материал в этом разделе во многом пересекается с изложенным выше. Но не задумывайтесь особо, почему мы решили разбить его именно таким образом. Просто многие вещи трудно точно классифицировать и организовать единственно правильным образом. Мы ставили себе задачу представить информацию в удобном для усвоения виде.