Выбрать главу

• Предыдущее предложение можно записать и так:

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. Рубизмы и идиомы

Материал в этом разделе во многом пересекается с изложенным выше. Но не задумывайтесь особо, почему мы решили разбить его именно таким образом. Просто многие вещи трудно точно классифицировать и организовать единственно правильным образом. Мы ставили себе задачу представить информацию в удобном для усвоения виде.

Ruby проектировался как непротиворечивый и ортогональный язык. Но вместе с тем это сложный язык, в котором есть свои идиомы и странности. Некоторые из них мы обсудим ниже.