Выражения управления
В Ruby существует множество способов управления выполнением. Все описанные здесь выражения возвращают значение.
Для тестов в этих выражениях управления nil и false являются ложными значениями, а true и любой другой объект — истинными значениями. В этом документе «истина» будет означать «истинное значение», а «ложь» — «ложное значение».
if Выражение
Самое простое if выражение состоит из двух частей: выражения «проверки» и выражения «тогда». Если выражение «проверки» вычисляется как истинное, то выполняется выражение «тогда».
Вот пример простого оператора if:
if true then puts "the test resulted in a true-value" end
Это выведет «тест дал истинное значение».
Часть then является необязательной:
if true puts "the test resulted in a true-value" end
В этом документе будет опущена необязательная часть then для всех выражений, так как это наиболее часто используемый способ использования if.
Вы также можете добавить выражение else. Если проверка не даёт истинное значение, выполнится выражение else:
if false puts "the test resulted in a true-value" else puts "the test resulted in a false-value" end
Это выведет «тест дал ложное значение».
Вы можете добавить любое количество дополнительных проверок в выражение if, используя elsif. Оператор elsif выполняется, когда все проверки выше elsif дают ложное значение.
a = 1 if a == 0 puts "a is zero" elsif a == 1 puts "a is one" else puts "a is some other value" end
Это выведет «a равно единице», так как 1 не равно 0. Так как else выполняется только в случае отсутствия совпадающих условий.
Как только условие совпадёт, либо условие if, либо любое условие elsif, выражение if завершается, и дальнейшие проверки не выполняются.
Как и в операторе if, условие elsif может быть дополнено then.
В этом примере будет выведено только «a равно единице»:
a = 1 if a == 0 puts "a is zero" elsif a == 1 puts "a is one" elsif a >= 1 puts "a is greater than or equal to one" else puts "a is some other value" end
Проверки для if и elsif могут иметь побочные эффекты. Наиболее распространённое использование побочных эффектов — кеширование значения в локальную переменную:
if a = object.some_value # do something to a end
Значение результата выражения if — это последнее выполненное значение в выражении.
Тernary if
Вы также можете записать выражение if-then-else, используя ? и :. Этот условный оператор if:
input_type = gets =~ /hello/i ? "greeting" : "other"
Эквивалентен следующему if выражению:
input_type =
if gets =~ /hello/i
"greeting"
else
"other"
end
Хотя условный оператор if намного короче, чем развернутая форма, для повышения читабельности рекомендуется использовать его только для простых условных выражений. Также следует избегать использования нескольких условных операторов if в одном выражении, так как это может привести к путанице.
unless Выражение
Выражение unless является противоположностью if выражению. Если значение ложно, выполняется выражение «тогда»:
unless true puts "the value is a false-value" end
Это ничего не выведет, так как true — это не ложное значение.
Вы можете использовать необязательное then с unless так же, как и в if.
Обратите внимание, что вышеприведённое unless выражение эквивалентно:
if not true puts "the value is a false-value" end
Как и в выражении if, вы можете использовать условие else с unless:
unless true puts "the value is false" else puts "the value is true" end
Это выводит «значение истинно» из условия else.
Вы не можете использовать elsif с выражением unless.
Значение результата выражения unless — это последнее выполненное значение в выражении.
Модификаторы if и unless
if и unless также могут быть использованы для модификации выражения. При использовании в качестве модификаторов левая часть является оператором «тогда», а правая — выражением «проверки»:
a = 0 a += 1 if a.zero? p a
Это выведет 1.
a = 0 a += 1 unless a.zero? p a
Это выведет 0.
Хотя модифицированные и стандартные версии имеют как выражение «проверки», так и выражение «тогда», они не являются точными преобразованиями друг друга из-за порядка разбора. Вот пример, который демонстрирует разницу:
p a if a = 0.zero?
Это вызывает NameError «неопределённая локальная переменная или метод `a'».
Когда ruby парсит это выражение, он сначала встречает a как вызов метода в выражении «тогда», а затем позже видит присвоение a в выражении «проверки» и помечает a как локальную переменную.
При выполнении этой строки он сначала выполняет выражение «проверки», a = 0.zero?.
Поскольку проверка истинна, он выполняет выражение «тогда», p a. Поскольку a в теле был записан как несуществующий метод, возникает NameError.
То же самое относится к unless.
case Выражение
Выражение case может использоваться двумя способами.
Наиболее распространённый способ — сравнить объект с несколькими шаблонами. Шаблоны сопоставляются с помощью метода +===+, который является псевдонимом для +==+ для Object. Другие классы должны переопределять его, чтобы обеспечить осмысленное поведение. См. Module#=== и Regexp#=== для примеров.
Вот пример использования case для сравнения String со шаблоном:
case "12345" when /^1/ puts "the string starts with one" else puts "I don't know what the string starts with" end
Здесь строка "12345" сравнивается с /^1/ путём вызова /^1/ === "12345", который возвращает true. Как и в if выражении, первое when совпадение выполняется, и все остальные совпадения игнорируются.
Если совпадений не найдено, выполняется else.
else и then являются необязательными. Это case выражение даёт тот же результат, что и предыдущее:
case "12345" when /^1/ puts "the string starts with one" end
Вы можете поместить несколько условий в то же when:
case "2" when /^1/, "2" puts "the string starts with one or is '2'" end
Ruby будет последовательно проверять каждое условие, поэтому сначала /^1/ === "2" возвращает false, а затем "2" === "2" возвращает true, поэтому выводится «строка начинается с единицы или равна '2'».
Вы можете использовать then после условия when . Это наиболее часто используется для размещения тела when на одной строке.
case a when 1, 2 then puts "a is one or two when 3 then puts "a is three" else puts "I don't know what a is" end
Другой способ использования выражения case — как выражение if-elsif:
a = 2 case when a == 1, a == 2 puts "a is one or two" when a == 3 puts "a is three" else puts "I don't know what a is" end
Опять же, then и else являются необязательными.
Значение результата выражения case — это последнее выполненное значение в выражении.
while Цикл
Цикл while выполняется, пока условие истинно:
a = 0 while a < 10 do p a a += 1 end p a
Выводит числа от 0 до 10. Условие a < 10 проверяется перед входом в цикл, затем выполняется тело, а затем условие проверяется снова. Когда условие приводит к ложному значению, цикл завершается.
Ключевое слово do является необязательным. Следующий цикл эквивалентен циклу выше:
while a < 10 p a a += 1 end
Результат цикла while равен nil , если не используется break для задания значения.
until Цикл
Цикл until выполняется, пока условие ложно:
a = 0 until a > 10 do p a a += 1 end p a
Это выводит числа от 0 до 11. Как и в цикле while, условие a > 10 проверяется при входе в цикл и каждый раз при выполнении тела цикла. Если условие ложно, цикл будет продолжаться.
Как и в цикле while, do является необязательным.
Как и в цикле while, результат цикла until равен nil, если не используется break.
for Цикл
Цикл for состоит из for , за которым следует переменная для хранения аргумента итерации, затем in и значение для итерации с помощью каждого. do является необязательным:
for value in [1, 2, 3] do puts value end
Выводит 1, 2 и 3.
Как и в while и until, do является необязательным.
Цикл for похож на использование each, но не создаёт новую область видимости переменных.
Значение результата цикла for — это перебираемое значение, если не используется break.
Цикл for редко используется в современных программах на Ruby.
Модификаторы while и until
Как и if и unless, while и until могут быть использованы в качестве модификаторов:
a = 0 a += 1 while a < 10 p a # prints 10
until в качестве модификатора:
a = 0 a += 1 until a > 10 p a # prints 11
Вы можете использовать begin и end для создания цикла while , который выполнит тело один раз перед проверкой условия:
a = 0 begin a += 1 end while a < 10 p a # prints 10
Если вы не используете rescue или ensure, Ruby оптимизирует обработку исключений.
break Оператор
Используйте break для раннего выхода из блока. Это остановит итерацию по элементам в values , если один из них чётный:
values.each do |value| break if value.even? # ... end
Вы также можете выйти из цикла while с помощью break:
a = 0 while true do p a a += 1 break if a < 10 end p a
Это выводит числа 0 и 1.
break принимает значение, которое задаёт результат выражения, из которого происходит выход:
result = [1, 2, 3].each do |value| break value * 2 if value.even? end p result # prints 4
next Оператор
Используйте next , чтобы пропустить остальную часть текущей итерации:
result = [1, 2, 3].map do |value| next if value.even? value * 2 end p result # prints [2, nil, 6]
next принимает аргумент, который может быть использован в качестве результата текущей итерации блока:
result = [1, 2, 3].map do |value| next value if value.even? value * 2 end p result # prints [2, 2, 6]
redo Оператор
Используйте redo , чтобы повторить текущую итерацию:
result = [] while result.length < 10 do result << result.length redo if result.last.even? result << result.length + 1 end p result
Это выводит [0, 1, 3, 3, 5, 5, 7, 7, 9, 9, 11]
В Ruby 1.8, вы также могли использовать retry, где вы использовали redo. Это больше неверно, теперь вы получите SyntaxError, когда используете retry вне блока rescue. Обратитесь к Исключениям для правильного использования retry.
Управляющие операторы
Грамматика Ruby различает операторы и выражения. Все выражения являются операторами (выражение — это тип оператора), но не все операторы являются выражениями. Некоторые части грамматики принимают выражения, а не другие типы операторов, что приводит к разному синтаксическому анализу похожего кода.
Например, когда они не используются в качестве модификаторов, if, else, while, until, и begin являются выражениями (и также операторами). Однако, когда они используются в качестве модификаторов, if, else, while, until и rescue являются операторами, но не выражениями.
if true; 1 end # expression (and therefore statement) 1 if true # statement (not expression)
Операторы, которые не являются выражениями, не могут быть использованы в контекстах, где ожидается выражение, таких как аргументы метода.
puts( 1 if true ) #=> SyntaxError
Вы можете заключить оператор в скобки, чтобы создать выражение.
puts((1 if true)) #=> 1
Если вы поставите пробел между именем метода и открывающей скобкой, вам не понадобятся две пары скобок.
puts (1 if true) #=> 1, because of optional parentheses for method
Это происходит потому, что это анализируется как вызов метода без скобок. Это эквивалентно следующему коду без создания локальной переменной:
x = (1 if true) p x
В операторе-модификаторе левая часть должна быть оператором, а правая — выражением.
Таким образом, в a if b rescue c, так как b rescue c является оператором, который не является выражением, и поэтому не допускается в качестве правой части оператора-модификатора if, код обязательно анализируется как (a if b) rescue c.
Это взаимодействует с приоритетом операторов таким образом, что:
stmt if v = expr rescue x stmt if v = expr unless x
анализируются как:
stmt if v = (expr rescue x) (stmt if v = expr) unless x
Это потому, что модификатор rescue имеет более высокий приоритет, чем =, а модификатор if имеет более низкий приоритет, чем =.
Триггер
Триггер — это редко используемое условное выражение. Его основное применение — обработка текста из однострочных программ Ruby, используемых с ruby -n или ruby -p.
Формат триггера — это выражение, указывающее, когда триггер включается, .. (или ...), а затем выражение, указывающее, когда триггер выключается. Пока триггер включен, он будет продолжать вычисляться в true, а false — когда выключен.
Вот пример:
selected = [] 0.upto 10 do |value| selected << value if value==2..value==8 end p selected # prints [2, 3, 4, 5, 6, 7, 8]
В приведенном выше примере условие включения — n==2. Триггер изначально выключен (false) для 0 и 1, но включается (true) для 2 и остается включенным до 8. После 8 он выключается и остается выключенным для 9 и 10.
Триггер должен использоваться внутри условного оператора, такого как if, while, unless, until и т. д., включая формы модификаторов.
При использовании включающего диапазона (..) условие выключения оценивается при изменении условия включения:
selected = [] 0.upto 5 do |value| selected << value if value==2..value==2 end p selected # prints [2]
Здесь обе стороны триггера оцениваются, поэтому триггер включается и выключается только тогда, когда value равно 2. Поскольку триггер включился в итерации, он возвращает true.
При использовании исключающего диапазона (...) условие выключения оценивается на следующей итерации:
selected = [] 0.upto 5 do |value| selected << value if value==2...value==2 end p selected # prints [2, 3, 4, 5]
Здесь триггер включается, когда value равно 2, но не выключается в той же итерации. Условие выключения оценивается только на следующей итерации, и value больше никогда не будет равно двум.
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.