Условный оператор в Ruby — это конструкция, которая выполняет разные ветки кода в зависимости от того, истинно ли выражение. В Ruby их несколько: if/elsif/else, unless, модификаторы в конце строки, тернарный оператор ?: и case/when.
Содержание
- Минимальный пример: if, elsif, else
- Что в Ruby считается истиной и ложью
- if как выражение и однострочная запись
- unless: условие с отрицанием
- Модификаторы: условие в конце строки
- Тернарный оператор
- case/when: выбор из многих вариантов
- Как выбрать конструкцию
- Частые ошибки
- Выводы
- Где применяется / связь с практикой
- FAQ
Две особенности Ruby отличают его от многих языков. Первая: if и case — выражения, они возвращают значение. Вторая: ложными считаются только nil и false, а 0, пустая строка и пустой массив — истинны. Ниже разберем каждую форму на запускаемых примерах и типичные ошибки новичков.
Минимальный пример: if, elsif, else
Начнем с полной программы, которую можно сохранить в файл ticket.rb и запустить командой ruby ticket.rb:
def ticket_price(age)
if age < 7
0
elsif age < 18
250
else
500
end
end
[5, 12, 30].each do |age|
puts "Возраст #{age}: #{ticket_price(age)} руб."
end
В консоли появятся три строки:
Возраст 5: 0 руб.
Возраст 12: 250 руб.
Возраст 30: 500 руб.
Разбор по шагам:
if age < 7проверяет первое условие. Если оно истинно, выполняется ветка до ближайшегоelsif/else, остальные пропускаются.elsif age < 18проверяется, только если все условия выше оказались ложными. Ветокelsifможет быть сколько угодно, порядок важен: сработает первая истинная.elseвыполняется, если не сработала ни одна ветка. Он необязателен.endзакрывает всю конструкцию — один на весьif, независимо от числаelsif.
Метод ticket_price не содержит return: в Ruby метод возвращает значение последнего вычисленного выражения, а if целиком и есть это выражение. Для age = 12 значение if равно 250.
Что в Ruby считается истиной и ложью
Условие в Ruby не обязано быть строго true или false. Проверяется любое значение, и правило одно: ложны только nil и false, все остальное истинно.
[nil, false, 0, "", [], "false"].each do |v|
puts "#{v.inspect} -> #{v ? 'истина' : 'ложь'}"
end
nil -> ложь
false -> ложь
0 -> истина
"" -> истина
[] -> истина
"false" -> истина
| Значение | Ruby | Python | JavaScript |
|---|---|---|---|
0 |
истина | ложь | ложь |
"" (пустая строка) |
истина | ложь | ложь |
[] (пустой массив) |
истина | ложь | истина |
nil / None / null |
ложь | ложь | ложь |
Эта разница — частый источник ошибок у тех, кто пришел из Python или JavaScript.
Неверно — проверять «есть ли что-то в массиве» самим значением или длиной:
items = []
if items.length
puts "Есть товары"
end
Результат: строка «Есть товары» печатается, хотя корзина пуста, потому что items.length равно 0, а 0 в Ruby истинен.
Исправление — спрашивать явно через методы-предикаты: items.empty?, items.any?, count.zero?, value.nil?.
items = []
if items.empty?
puts "Корзина пуста"
end
# => Корзина пуста
if как выражение и однострочная запись
Раз if возвращает значение, его можно присвоить переменной. Если ни одна ветка не сработала и else нет, результатом будет nil.
temp = 25
status = if temp > 30 then "жарко" else "комфортно" end
puts status # => комфортно
x = if false then 1 end
p x # => nil
Ключевое слово then нужно, когда условие и ветка стоят на одной строке. В многострочной записи его обычно не пишут.
unless: условие с отрицанием
unless выполняет ветку, когда условие ложно. Это то же самое, что if !условие, но читается ближе к обычной речи.
balance = 0
puts "Нужно пополнить счет" unless balance.positive?
# => Нужно пополнить счет
У unless есть ветка else, но нет elsif. Практическое правило, которого придерживается большинство Ruby-стайлгайдов: unless хорош для одного простого условия без else. Двойное отрицание вроде unless !ready или unless ... else проще переписать через if.
Модификаторы: условие в конце строки
Короткую ветку из одной инструкции можно записать с условием после нее:
temp = 25
puts "Тепло" if temp > 20 # => Тепло
puts "Не мороз" unless temp < 0 # => Не мороз
Чаще всего модификаторы используют для guard clause — раннего выхода из метода, чтобы основная логика не пряталась в глубокой вложенности:
def discount(user)
return 0 if user.nil?
return 0 unless user[:active]
user[:orders] > 10 ? 15 : 5
end
p discount(nil) # => 0
p discount({ active: false, orders: 20 }) # => 0
p discount({ active: true, orders: 20 }) # => 15
p discount({ active: true, orders: 3 }) # => 5
Граница приема: модификатор подходит для одной короткой инструкции. Если ветка длиннее строки или в ней несколько действий, лучше обычный if ... end.
Тернарный оператор
Тернарный оператор условие ? значение_если_истина : значение_если_ложь — компактная запись if/else для выбора одного из двух значений:
temp = 25
label = temp >= 18 ? "без куртки" : "в куртке"
puts label # => без куртки
Он удобен внутри выражений и интерполяции строк. Вложенные тернарные операторы (a ? b : c ? d : e) читаются плохо — для трех и более вариантов берите if/elsif или case.
case/when: выбор из многих вариантов
case сравнивает одно значение с набором вариантов. Сравнение в when идет не через ==, а через оператор === (case equality), поведение которого определяет класс слева. Поэтому в when можно писать диапазоны, классы и регулярные выражения:
def grade(score)
case score
when 90..100 then "отлично"
when 75...90 then "хорошо"
when 60...75 then "удовлетворительно"
when Integer then "неудовлетворительно"
else "некорректный балл"
end
end
[95, 90, 74, 10, "abc"].each { |s| puts "#{s.inspect}: #{grade(s)}" }
95: отлично
90: отлично
74: удовлетворительно
10: неудовлетворительно
"abc": некорректный балл
Что здесь происходит:
90..100— диапазон с включенной правой границей,75...90— без нее, поэтому90попадает в «отлично», а не в «хорошо». Скобки вокруг диапазона вwhenне нужны.when Integerсрабатывает для любого целого числа:Integer === 10возвращаетtrue.- Строка
"abc"не подошла ни к одному варианту и ушла вelse.
| Что стоит в when | Как работает === |
Пример |
|---|---|---|
| Значение | равенство | when "admin" |
| Диапазон | входит ли в диапазон | when 1..5 |
| Класс | является ли экземпляром класса | when String |
| Регулярное выражение | совпадает ли строка с шаблоном | when /\A\d{6}\z/ |
| Несколько значений через запятую | совпадение с любым из них | when Integer, Float |
В отличие от switch в C или JavaScript, в Ruby нет «проваливания» в следующую ветку: выполняется только первая подходящая when, break не нужен. case без значения после него (case и сразу when условие) работает как цепочка if/elsif.
В Ruby 3.x есть еще форма case/in — сопоставление с образцом (pattern matching) для разбора хешей и массивов. Это отдельная тема, здесь ограничимся примером:
case { name: "Ann", age: 20 }
in { name: String => name, age: Integer => age } if age >= 18
puts "#{name}: совершеннолетняя"
end
# => Ann: совершеннолетняя
Как выбрать конструкцию
| Задача | Что взять |
|---|---|
| Одно условие, одна короткая инструкция | модификатор if/unless |
| Ранний выход из метода | return ... if ... (guard clause) |
| Выбрать одно из двух значений | тернарный оператор |
| Две-три ветки с разными условиями | if/elsif/else |
| Одно значение против многих вариантов, диапазонов, классов | case/when |
| Разбор структуры хеша или массива (Ruby 3.x) | case/in |
Частые ошибки
else if вместо elsif. Код else if x > 1 открывает новый вложенный if, которому нужен свой end. С одним end Ruby завершится синтаксической ошибкой (в Ruby 2.6 текст такой: syntax error, unexpected end-of-input, expecting end, в новых версиях формулировка другая). Исправление — писать elsif одним словом.
= вместо == в условии. if name = "Python" не сравнивает, а присваивает; результат присваивания — строка, она истинна, и ветка выполняется всегда. Ruby при этом печатает предупреждение found '= literal' in conditional, should be ==. Исправление — if name == "Python".
and/or вместо &&/|| в присваивании. У слов and и or приоритет ниже, чем у =:
ok = true and false
p ok # => true
ok = true && false
p ok # => false
В первой строке сначала выполнилось ok = true, а and false вычислилось отдельно. В условиях используйте &&, || и !.
Выводы
- В Ruby ложны только
nilиfalse;0,""и[]истинны, поэтому пустоту проверяют методамиempty?,zero?,nil?. ifиcase— выражения: их результат можно присвоить или вернуть из метода безreturn.elsifпишется одним словом,unlessлучше использовать безelse, модификаторы — для одной короткой инструкции и guard clause.case/whenсравнивает через===, поэтому принимает диапазоны, классы и регулярные выражения, и не проваливается в следующую ветку.
Где применяется / связь с практикой
Ветвления есть в любой программе: валидация ввода, расчет цены, маршрутизация запроса, обработка ошибок. Логика, которую вы отработали на Ruby, — истинность условия, порядок проверок, ранний выход, выбор между if и case — переносится на другие языки почти без изменений, меняется в основном синтаксис и правило истинности.
Освойте тему на практике
Отдельного курса по Ruby у Otus сейчас нет. Если цель — войти в разработку с нуля, удобный старт — Python: близкая по духу выразительность и широкий спрос. Разобрать условия, циклы и функции с наставником можно на курсе Python Developer. Basic, а познакомиться с преподавателями и форматом — на бесплатных открытых уроках Otus.
FAQ
Есть ли в Ruby оператор switch?
Ключевого слова switch нет, его роль выполняет case/when. Сравнение в нем идет через ===, а break между ветками не нужен.
Можно ли писать условие в скобках, как в C?
Можно: if (x > 5) работает, но в Ruby-коде скобки вокруг условия обычно опускают. Они полезны, когда нужно явно сгруппировать сложное выражение.
Чем nil? отличается от проверки if value?
if value ложно и для nil, и для false. value.nil? истинно только для nil, поэтому различает «значения нет» и «значение равно false».



