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]