• В качестве примера рассмотрим строку 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 проектировался как непротиворечивый и ортогональный язык. Но вместе с тем это сложный язык, в котором есть свои идиомы и странности. Некоторые из них мы обсудим ниже.
• С помощью ключевого слова alias
можно давать глобальным переменным и методам альтернативные имена (синонимы).
• Пронумерованные глобальные переменные $1
, $2
, $3
и т.д. не могут иметь синонимов.
• Мы не рекомендуем использовать «специальные переменные» $=
, $_
, $/
и им подобные. Иногда они позволяют написать более компактный код, но при этом он не становится более понятным. Поэтому в данной книге мы прибегаем к ним очень редко, что и вам рекомендуем.
• Не путайте операторы диапазона ..
и ...
— первый включает верхнюю границу, второй исключает. Так, диапазон 5..10
включает число 10, а диапазон 5...10
— нет.
• С диапазонами связана одна мелкая деталь, которая может вызвать путаницу. Если дан диапазон m..n
, то метод end
вернет конечную его точку n
, равно как и его синоним last
. Но те же методы возвращают значение n
и для диапазона m...n
, хотя n
не включается в него. Чтобы различить эти две ситуации, предоставляется метод end_excluded?
.
• Не путайте диапазоны с массивами. Следующие два присваивания абсолютно различны:
x = 1..5
x = [1, 2, 3, 4, 5]
Однако есть удобный метод to_a
для преобразования диапазона в массив. (Во многих других типах тоже есть такой метод.)