Spec-Zone.ru › Ruby 3

Выражения управления

В 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 — это последнее выполненное значение в выражении.

С Ruby 2.7, выражения case также предоставляют более мощную экспериментальную функцию сопоставления шаблонов с помощью ключевого слова in:

case {a: 1, b: 2, c: 3}
in a: Integer => m
  "matched: #{m}"
else
  "not matched"
end
# => "matched: 1"

Синтаксис сопоставления шаблонов описан на своей собственной странице.

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 и значение для итерации с помощью each. Ключевое слово 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–2020 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API