Условия и ввод/вывод

Предпосылки: Переменные и типы (переменные, присваивание, примитивные типы).

Переменные и типы | Циклы

Маркетплейс: менеджер открывает карточку продавца и хочет понять, можно ли проводить выплату. У продавца есть имя, баланс и признак блокировки:

name = "Alice"
balance = 1580
blocked = false

Программа из предыдущей заметки умеет хранить эти три значения и считать. Но выплату нужно проводить не всегда: заблокированному продавцу переводить нельзя, продавцу с нулевым балансом — бессмысленно. Для человека правило «если заблокирован — пропустить» очевидно сразу. В коде его нужно записать явно.

Ветвление: if и else

if позволяет выполнить один блок кода или другой в зависимости от условия:

if blocked
  puts "Продавец заблокирован, выплата отменена"
else
  puts "Можно проводить выплату"
end

Если blocked истинно, выполняется первый блок. Иначе — блок после else. puts здесь печатает строку в терминал, чтобы менеджер увидел решение; подробнее — в разделе «Ввод и вывод» ниже.

Условие — это выражение, которое даёт true или false. У нас переменная blocked уже логическая, поэтому её можно поставить прямо в if. Но чаще условие — это сравнение:

balance > 0
balance == 0
name != ""

== проверяет равенство. >, <, >=, <=, != сравнивают значения. Результат такого сравнения — true или false, то самое, что if умеет различать.

Составные условия

Одного сравнения часто мало. Менеджер хочет проводить выплату, только если продавец активен и баланс положителен:

if !blocked && balance > 0
  puts "Можно проводить выплату"
else
  puts "Выплата невозможна"
end

&& означает «и»: оба условия должны быть истинны. || означает «или»: достаточно одного. ! означает «не» и переворачивает результат. !blocked читается как «не заблокирован».

Составные условия удобно строить, комбинируя все три операции. Например, если у маркетплейса есть VIP-продавцы, которым разрешено уходить в минус до -500, правило можно записать так:

vip = true
 
if !blocked && (balance > 0 || vip && balance > -500)
  puts "Можно проводить выплату"
end

Скобки здесь задают порядок проверки, как в арифметике.

Несколько вариантов подряд

Пока что менеджер видел два ответа — «можно» и «нельзя». В жизни причин отказа несколько, и хочется показывать точную:

if blocked
  puts "Продавец заблокирован"
elsif balance == 0
  puts "Нулевой баланс, выплачивать нечего"
elsif balance < 0
  puts "Отрицательный баланс, сначала пополнение"
else
  puts "Можно проводить выплату"
end

elsif позволяет перечислить несколько взаимоисключающих случаев. Проверка идёт сверху вниз. Как только найден первый подходящий вариант, остальные блоки пропускаются — и это важно: если продавец заблокирован, программа уже не будет смотреть на баланс, потому что первая ветка закрыла вопрос.

В ассемблере то же ветвление строилось из CMP и условных переходов. Здесь условие видно прямо в тексте, и читать его проще: не нужно помнить, что именно сравнивали в предыдущей строке и куда ведёт прыжок.

Ввод и вывод

До сих пор значения name, balance и blocked были записаны прямо в коде. Но карточку продавца менеджер открывает не ту, что автор придумал заранее. Программа должна уметь получать данные извне — от пользователя, из файла, из сети — и возвращать результат наружу.

Вывод — показать результат человеку. В Ruby для этого используют puts (от put string — «вывести строку»):

puts "Итоговый баланс: 1580"
puts balance

В терминале появится две строки: сначала готовый текст, потом число из переменной.

Ввод — прочитать данные от пользователя. В Ruby для этого используют gets (от get string — «получить строку»). Менеджер вводит имя продавца, а программа обращается к нему по имени:

puts "Имя продавца:"
name = gets
puts "Карточка: "
puts name

После gets в переменную попадает не только то, что напечатал пользователь, но и символ перехода на следующую строку — тот самый, что Enter добавляет в конец. В коде этот символ записывается как \n. Если просто напечатать name, это почти не видно, но при попытке сравнить такую строку с чем-то подобная мелочь ломает всё. Представим, что менеджер вводит статус продавца:

puts "Статус (active/blocked):"
status = gets
 
if status == "active"
  puts "Продавец активен"
else
  puts "Продавец неактивен или неизвестен"
end

Менеджер вводит active, жмёт Enter — и видит «неактивен». Причина в том, что в status лежит не "active", а "active\n". Условие status == "active" честно возвращает false: строки разные.

Лекарство — метод chomp, который отрезает служебный перевод строки с конца ввода:

puts "Статус (active/blocked):"
status = gets.chomp
 
if status == "active"
  puts "Продавец активен"
else
  puts "Продавец неактивен или неизвестен"
end

Теперь в status лежит ровно "active", сравнение срабатывает, менеджер получает правильный ответ. Дальше такую же очистку ввода делают почти всегда, пока это не становится привычкой.

Карточка продавца замкнулась: данные пришли через ввод, условие выбрало нужную ветку, результат ушёл на вывод.

А теперь менеджер хочет проверить не одного продавца, а всех сразу — тысячу карточек подряд, одинаковой логикой. Писать тысячу копий if вручную не вариант. Нужен способ сказать коду: «повтори эту проверку для каждого». Этим занимаются циклы.

Sources

  • Thomas, D. et al., 2023, Programming Ruby 3.3. Pragmatic Bookshelf.
  • Sammet, J., 1969, Programming Languages: History and Fundamentals. Prentice-Hall.

Переменные и типы | Циклы