Ruby Language
Поток управления
Поиск…
if, elsif, else и end
Ruby предлагает ожидаемые выражения if и else для логики ветвления, завершенные ключевым словом end :
# Simulate flipping a coin
result = [:heads, :tails].sample
if result == :heads
puts 'The coin-toss came up "heads"'
else
puts 'The coin-toss came up "tails"'
end
В Ruby, if операторы являются выражениями, которые оценивают значение, и результат может быть назначен переменной:
status = if age < 18
:minor
else
:adult
end
Ruby также предлагает тройные операторы C-стиля ( подробнее см. Здесь ), которые могут быть выражены как:
some_statement ? if_true : if_false
Это означает, что приведенный выше пример с использованием if-else также может быть записан как
status = age < 18 ? :minor : :adult
Кроме того, Ruby предлагает ключевое слово elsif которое принимает выражение для включения дополнительной логики ветвления:
label = if shirt_size == :s
'small'
elsif shirt_size == :m
'medium'
elsif shirt_size == :l
'large'
else
'unknown size'
end
Если ни одно из условий в цепочке if / elsif является истинным, и нет предложения else , тогда выражение оценивается как nil. Это может быть полезно внутри интерполяции строк, поскольку nil.to_s - это пустая строка:
"user#{'s' if @users.size != 1}"
Значения Truthy и Falsy
В Ruby существует ровно два значения, которые считаются «ложными» и будут возвращать false при проверке как условие для выражения if . Они есть:
-
nil - boolean
false
Все остальные значения считаются «правдивыми», в том числе:
-
0- числовой ноль (целое или иное) -
""- Пустые строки -
"\n"- Строки, содержащие только пробелы -
[]- пустые массивы -
{}- Пустые хеши
Возьмем, к примеру, следующий код:
def check_truthy(var_name, var)
is_truthy = var ? "truthy" : "falsy"
puts "#{var_name} is #{is_truthy}"
end
check_truthy("false", false)
check_truthy("nil", nil)
check_truthy("0", 0)
check_truthy("empty string", "")
check_truthy("\\n", "\n")
check_truthy("empty array", [])
check_truthy("empty hash", {})
Вывод:
false is falsy
nil is falsy
0 is truthy
empty string is truthy
\n is truthy
empty array is truthy
empty hash is truthy
пока, пока
В while цикл выполняется блок , пока данное условие:
i = 0
while i < 5
puts "Iteration ##{i}"
i +=1
end
until цикл не выполнит блок, а условие - false:
i = 0
until i == 5
puts "Iteration ##{i}"
i +=1
end
Встроенный if / if
Общим примером является использование встроенного или конечного, if или unless :
puts "x is less than 5" if x < 5
Это называется условным модификатором и является удобным способом добавления простого защитного кода и ранних возвратов:
def save_to_file(data, filename)
raise "no filename given" if filename.empty?
return false unless data.valid?
File.write(filename, data)
end
К этим модификаторам нельзя добавить предложение else . Также обычно не рекомендуется использовать условные модификаторы внутри основной логики. Для сложного кода следует использовать нормальный if , elsif , else вместо.
если
Общим утверждением является if !(some condition) . Рубин предлагает альтернативу в unless , unless заявление.
Структура точно такая же, как и оператор if , за исключением того, что условие отрицательное. Кроме того , unless оператор не поддерживает elsif , но он поддерживает else :
# Prints not inclusive
unless 'hellow'.include?('all')
puts 'not inclusive'
end
Заявление о ситуации
Ruby использует ключевое слово case для операторов switch.
Согласно Ruby Docs :
Операторы case состоят из необязательного условия, которое находится в позиции аргумента к
case, и ноль или болееwhenклаузе. Параметр firstwhen, чтобы соответствовать условию (или для оценки логической истины, если условие равно null) «выигрывает», и выполняется его строфа кода. Значение аргумента case - это значение успешного предложенияwhen, илиnilесли такого предложения нет.Оператор case может закончиться предложением
else. Каждый,whenоператор может иметь несколько значений кандидата, разделенных запятыми.
Пример:
case x
when 1,2,3
puts "1, 2, or 3"
when 10
puts "10"
else
puts "Some other number"
end
Более короткая версия:
case x
when 1,2,3 then puts "1, 2, or 3"
when 10 then puts "10"
else puts "Some other number"
end
Значение case п сравниваются с каждым , when положение с использованием === методы (не == ). Поэтому его можно использовать с различными типами объектов.
Оператор case может использоваться с диапазонами :
case 17
when 13..19
puts "teenager"
end
Оператор case может использоваться с Regexp :
case "google"
when /oo/
puts "word contains oo"
end
Оператор case может использоваться с Proc или лямбдой:
case 44
when -> (n) { n.even? or n < 0 }
puts "even or less than zero"
end
Оператор case может использоваться с классами :
case x
when Integer
puts "It's an integer"
when String
puts "It's a string"
end
Внедряя метод === вы можете создавать свои собственные классы соответствия:
class Empty
def self.===(object)
!object or "" == object
end
end
case ""
when Empty
puts "name was empty"
else
puts "name is not empty"
end
Оператор case может использоваться без значения для соответствия:
case
when ENV['A'] == 'Y'
puts 'A'
when ENV['B'] == 'Y'
puts 'B'
else
puts 'Neither A nor B'
end
Оператор case имеет значение, поэтому вы можете использовать его как аргумент метода или в задании:
description = case 16
when 13..19 then "teenager"
else ""
end
Управление контуром с перерывом, затем и повтор
Поток выполнения блока Ruby может контролироваться с помощью break , next и redo операторов.
break
Оператор break немедленно выйдет из блока. Любые оставшиеся инструкции в блоке будут пропущены, и итерация закончится:
actions = %w(run jump swim exit macarena)
index = 0
while index < actions.length
action = actions[index]
break if action == "exit"
index += 1
puts "Currently doing this action: #{action}"
end
# Currently doing this action: run
# Currently doing this action: jump
# Currently doing this action: swim
next
next оператор немедленно вернется в верхнюю часть блока и продолжит следующую итерацию. Любые оставшиеся команды в блоке будут пропущены:
actions = %w(run jump swim rest macarena)
index = 0
while index < actions.length
action = actions[index]
index += 1
next if action == "rest"
puts "Currently doing this action: #{action}"
end
# Currently doing this action: run
# Currently doing this action: jump
# Currently doing this action: swim
# Currently doing this action: macarena
redo
Оператор redo немедленно вернется в начало блока и повторит ту же самую итерацию. Любые оставшиеся команды в блоке будут пропущены:
actions = %w(run jump swim sleep macarena)
index = 0
repeat_count = 0
while index < actions.length
action = actions[index]
puts "Currently doing this action: #{action}"
if action == "sleep"
repeat_count += 1
redo if repeat_count < 3
end
index += 1
end
# Currently doing this action: run
# Currently doing this action: jump
# Currently doing this action: swim
# Currently doing this action: sleep
# Currently doing this action: sleep
# Currently doing this action: sleep
# Currently doing this action: macarena
Enumerable итерация
В дополнение к циклам эти операторы работают с методами перечислимого итерации, такими как each и map :
[1, 2, 3].each do |item|
next if item.even?
puts "Item: #{item}"
end
# Item: 1
# Item: 3
Заблокировать значения результата
В обоих break и next значение может быть предоставлено и будет использоваться как значение результата блока:
even_value = for value in [1, 2, 3]
break value if value.even?
end
puts "The first even value is: #{even_value}"
# The first even value is: 2
бросать, ловить
В отличие от многих других языков программирования ключевые слова throw and catch не связаны с обработкой исключений в Ruby.
В Ruby, throw и catch действуют как ярлыки на других языках. Они используются для изменения потока управления, но не связаны с понятием «ошибка», например «Исключения».
catch(:out) do
catch(:nested) do
puts "nested"
end
puts "before"
throw :out
puts "will not be executed"
end
puts "after"
# prints "nested", "before", "after"
Управляющий поток с логическими инструкциями
Хотя это может показаться нелогичным, вы можете использовать логические операторы для определения того, выполняется ли оператор. Например:
File.exist?(filename) or STDERR.puts "#{filename} does not exist!"
Это проверит, существует ли файл, и только распечатывает сообщение об ошибке, если это не так. Оператор or является ленивым, что означает, что он перестанет выполняться, если он уверен, что значение true или false. Как только первый термин окажется истинным, нет необходимости проверять значение другого термина. Но если первый член является ложным, он должен проверить второй член.
Обычно используется значение по умолчанию:
glass = glass or 'full' # Optimist!
Это устанавливает значение «full» для glass если оно еще не установлено. Более кратко, вы можете использовать символическую версию or :
glass ||= 'empty' # Pessimist.
Также можно запустить второй оператор только в том случае, если первый из них является ложным:
File.exist?(filename) and puts "#{filename} found!"
Опять же, and ленив, поэтому он будет выполнять только второй оператор, если необходимо, чтобы получить значение.
Оператор or имеет более низкий приоритет, чем and . Аналогично, || имеет более низкий приоритет, чем && . Формы символов имеют более высокий приоритет, чем словоформы. Это удобно знать, когда вы хотите смешать эту технику с назначением:
a = 1 and b = 2
#=> a==1
#=> b==2
a = 1 && b = 2; puts a, b
#=> a==2
#=> b==2
Обратите внимание, что руководство по стилю Ruby рекомендует :
Запрещены
andи /orключевые слова. Минимальная добавленная читаемость просто не стоит высокой вероятности введения тонких ошибок. Для булевых выражений всегда используйте&&и||вместо. Для управления потоком используйте,ifиunless;&&и||также приемлемы, но менее ясны.
начинать, заканчивать
begin блок - это структура управления, объединяющая несколько операторов.
begin
a = 7
b = 6
a * b
end
begin блок вернет значение последнего оператора в блоке. Следующий пример вернет 3 .
begin
1
2
3
end
begin блок полезен для условного присваивания с помощью оператора ||= где для возврата результата может потребоваться несколько операторов.
circumference ||=
begin
radius = 7
tau = Math::PI * 2
tau * radius
end
Он также может быть объединен с другими блочными структурами, такими как rescue , ensure , while if , unless , и т. Д., Чтобы обеспечить больший контроль над потоком программы.
Begin блоки не кодовые блоки, например { ... } или do ... end ; они не могут быть переданы в функции.
return vs. next: нелокальное возвращение в блок
Рассмотрим этот сломанный фрагмент:
def foo
bar = [1, 2, 3, 4].map do |x|
return 0 if x.even?
x
end
puts 'baz'
bar
end
foo # => 0
Можно было бы ожидать return , чтобы получить значение для map массива «s результатов блока. Таким образом, возвращаемое значение foo будет [1, 0, 3, 0] . Вместо этого return возвращает значение из метода foo . Обратите внимание, что baz не печатается, что означает, что выполнение никогда не достигало этой строки.
next со значением делает трюк. Он действует как return уровне блока.
def foo
bar = [1, 2, 3, 4].map do |x|
next 0 if x.even?
x
end
puts 'baz'
bar
end
foo # baz
# => [1, 0, 3, 0]
В отсутствие return значение, возвращаемое блоком, является значением его последнего выражения.
Оператор Or-Equals / Conditional присваивания (|| =)
Ruby имеет оператор or-equals, который позволяет присваивать значение переменной тогда и только тогда, когда эта переменная оценивается как nil и false .
||= # this is the operator that achieves this.
этот оператор с двойными трубами, представляющими знак или знак равенства, представляющий присвоение значения. Вы можете подумать, что это что-то вроде этого:
x = x || y
этот вышеприведенный пример неверен. Оператор or-equals фактически представляет это:
x || x = y
Если x вычисляет значение nil или false то x присваивается значение y и в противном случае остается неизменным.
Вот практический пример использования оператора or-equals. Представьте, что у вас есть часть вашего кода, которая, как ожидается, отправит электронное письмо пользователю. Что вы делаете, если по какой-либо причине нет электронной почты для этого пользователя. Вы могли бы написать что-то вроде этого:
if user_email.nil?
user_email = "[email protected]"
end
Используя оператор or-equals, мы можем разрезать весь этот фрагмент кода, обеспечивая чистый, четкий контроль и функциональность.
user_email ||= "[email protected]"
В случаях, когда false является допустимым значением, необходимо соблюдать осторожность, чтобы не переопределять его случайно:
has_been_run = false
has_been_run ||= true
#=> true
has_been_run = false
has_been_run = true if has_been_run.nil?
#=> false
Тернарный оператор
Ruby имеет тернарный оператор ( ?: :), Который возвращает одно из двух значений, основанное на том, что условие оценивается как правдивое:
conditional ? value_if_truthy : value_if_falsy
value = true
value ? "true" : "false"
#=> "true"
value = false
value ? "true" : "false"
#=> "false"
это то же самое, что и запись, if a then b else c end , хотя предпочтительна тройка
Примеры:
puts (if 1 then 2 else 3 end) # => 2
puts 1 ? 2 : 3 # => 2
x = if 1 then 2 else 3 end
puts x # => 2
Флип-флоп-оператор
Оператор flip flop .. используется между двумя условиями в условном выражении:
(1..5).select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 3, 4]
Условие оценивается как false пока первая часть не станет true . Затем он оценивает значение true пока вторая часть не станет true . После этого он снова переключится на значение false .
Этот пример иллюстрирует, что выбирается:
[1, 2, 2, 3, 4, 4, 5].select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 2, 3, 4]
Оператор триггера работает только внутри ifs (включая unless ) и тройного оператора. В противном случае он рассматривается как оператор диапазона.
(1..5).select do |e|
(e == 2) .. (e == 4)
end
# => ArgumentError: bad value for range
Он может переключаться с false на true и обратные несколько раз:
((1..5).to_a * 2).select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 3, 4, 2, 3, 4]