Глава восьма
Умовні оператори…
Комп’ютерні програми, як і саме життя, сповнені складних рішень, які потрібно приймати. Такі речі, як: якщо я лежатиму більше у ліжку, я довше спатиму, інакше мені потрібно буде йти на роботу; якщо я піду на роботу, я зароблю гроші, інакше я втрачу свою роботу - і так далі…
Ми вже виконували декілька if
–перевірок у попередніх програмах. Ось простий приклад з обчислювача податку з першої глави:
if (subtotal < 0.0) then
subtotal = 0.0
end
Ця програма просила користувача ввести значення subtotal
, яке використовувалось для обчислення податку. Маленька перевірка вище страхує нас від того, що subtotal
буде від’ємним. Якщо користувач, в божевільному пориві введе значення, яке менше за 0
, перевірка if
це помітить, оскільки (subtotal < 0.0
) буде істинним, а це спричинить до того, що код між перевіркою if
та ключовим словом end
виконається. У нашому випадку він встановить значення subtotal
рівне 0
.
Одне дорівнює
=
чи два==
?Як і у більшості мовах програмування, Ruby використовує один знак рівності для присвоєння значення
=
, а два — для порівняння значень==
.
if..then..else
Проста перевірка if
має лише два можливих результати. Код або виконається, або ні, в залежності від істиності або хибності перевірки.
Часто вам потрібно мати більше, ніж два можливих результати. Давайте припустимо, наприклад, що програма повинна виконуватись одним способом, якщо день тижня будній та іншим — якщо вихідним. Ви можете перевірити це, додавши секцію else
після секції if
, ось так:
if aDay == 'Субота' or aDay == 'Неділя'
daytype = 'вихідний'
else
daytype = 'будень'
end
Тут умова if
є простою. Вона перевіряє дві можливі умови:
- значення змінної
aDay
рівне рядку'Субота'
або… - значення змінної
aDay
рівне рядку'неділя'
.
Якщо якась з цих умов є істинною, тоді виконується наступний рядок:
daytype = 'вихідний'
У всіх інших випадках виконується код після else
:
daytype = 'будень'
Коли перевірка
if
та код, який треба виконати, знаходяться на різних рядках, ключове словоthen
є необов’язковим. Коли перевірка і код розміщені на одному рядку, між ними обов’язково має стояти ключове словоthen
(або, якщо ви надаєте перевагу лаконічному коду, двокрапка):
if x == 1 then puts('ok') end # з 'then' if x == 1 : puts('ok') end # з двокрапкою if x == 1 puts('ok') end # синтаксична помилка!
Перевірка if
не обмежена виконанням лише двох умов. Припустимо, наприклад, що ваш код має розуміти, чи певний день є робочим днем чи святковим. Всі будні є робочими днями; всі суботи є святковими днями, але неділі є лише святами, коли ви не працюєте понаднормово.
Примітка перекладача:
У прикладі вище наведений переклад оригінального тексту. Автор книги — американець, а в Сполучених Штатах Америки неділя вважається першим днем тижня.
Це моя перша спроба написати перевірку, яка задовольнятиме цим умовам:
working_overtime = true
if aDay == 'Субота' or aDay == 'Наділя' and not working_overtime
daytype = 'свято'
puts( "Уурраа!" )
else
daytype = 'робочий день'
end
На жаль, це не працює достатньо добре. Пам’ятайте, що субота є завжди святом. Проте цей код наполягає, що 'субота'
— робочий день. Це стається тому, що Ruby сприймає перевірку так: “Якщо цей день — субота і я не працюю понаднормово, або якщо цей день — неділя, і я не працюю понаднормово” — проте насправді я мав на увазі: “Якщо це день — субота або якщо цей день — неділя, і я не працюю понаднормово”.
Найпростіший спосіб вирішити цю неточність — огорнути перевірки, які мають трактуватись як одне ціле, в круглі дужки, ось так:
if aDay == 'Субота' or (aDay == 'Неділя' and not working_overtime)
and
, or
, not
Між іншим, Ruby має два різні записи для булевих умов.
У прикладі, наведеному вище, я використав оператори у вигляді англійських слів: and
(і/та), or
(або) та not
(ні). Якщо хочете, ви можете використовувати альтернативні оператори, подібні до тих, які є у інших мовах програмування, а саме: &&
(і/та), ||
(або) та !
(ні).
Тим не менше, будьте обережні, ці дві групи операторів не повністю взаємозамінні. Зокрема, вони мають різну пріоритетність, а це означає, що при використанні різних операторів у перевірці, її частини можуть виконуватись по–різному в залежності від того, який оператор ви використовуєте.
if..elsif
Без сумнівно бувають ситуації, коли вам необхідно виконати різні дії для різних альтернативних умов. Один зі способів зробити це — виконання умови if
, після якої слідуватиме серія інших перевірок з ключовими словами elsif
. Після всіх перевірок мусить стояти ключове слово end
.
Наприклад, тут я почергово, в циклі while
, прошу користувача ввести дані. Умова if
перевіряє, чи користувач ввів 'q'
(я використовую метод chomp()
, щоб видалити небажані символи з вводу). Якщо 'q'
не введено, перша умова elsif
перевіряє, чи цілочисельне значення введених даних (input.to_i
) більше за 800. Якщо результат перевірки є хибним наступна умова elsif
перевіряє, чи цілочисельне значення є меншим або більшим за 800:
while input != 'q' do
puts("Введіть число від 1 до 1000 (або 'q' для виходу)")
print("?- ")
input = gets().chomp()
if input == 'q'
puts("Бувай")
elsif input.to_i > 800
puts("Це зависокий рівень оплати!")
elsif input.to_i <= 800
puts("Це ми потягнемо")
end
end
Цей код має баг. Він просить ввести число від 1 до 1000, проте приймає й інші числа. Давайте спробуємо переписати цю перевірку без помилок!
Ruby також має скорочену форму запису
if..then..else
в якій знак питання?
заміняєif..then
, а двокрапка поводиться якelse
…<умова> ? <якщо істина> : <якщо хиба>
Наприклад:
x == 10 ? puts("це 10") : puts("це якесь інше число")
Коли перевірка умови є комплексною (якщо вона використовує
and
таor
), вам слід огорнути її у дужки.Якщо перевірка та код займають декілька рядків,
?
має стояти на тому ж рядку, що й умова, що передує йому, а:
повинна бути на тому ж рядку, що і код, який йде після?
.Іншими словами, якщо ви розмістити перехід на новий рядок перед
?
або:
, ви отримаєте синтаксичну помилку. Ось приклад правильного багаторядкового блоку коду:
(aDay == 'Субота' or aDay == 'Неділя') ? daytype = 'вихідний' : daytype = 'будень'
unless
Ruby також може виконувати перевірку unless
, яка є протилежною до перевірки if
:
unless aDay == 'Субота' or aDay == 'Неділя'
daytype = 'вихідний'
else
daytype = 'будень'
end
Думайте про unless
як про альтернативний спосіб перевірити якщо не. Ось відповідний еквівалент коду наведеного вище:
if !(aDay == 'Субота' or aDay == 'Неділя')
daytype = 'вихідний'
else
daytype = 'будень'
end
Модифікатори if
та unless
Ви напевно пригадуєте альтернативний запис для циклу while
з Глави 7. Замість того, щоб писати так…
while tired do sleep end # поки втомлений спати
…ми можемо переписати це ось так:
sleep while tired # спати поки втомлений
Альтернативний запис, при якому ключове слово while
розташоване між кодом, який треба виконати та тестовою умовою, називається модифікатор while
. Так от, Ruby також має модифікатори if
та unless
. Ось кілька прикладів:
sleep if tired # спати якщо втомлений
begin #
sleep # спати
snore # хропіти
end if tired # якщо втомлений
sleep unless not tired # не спати якщо не втомлений
begin #
sleep # спати
snore # хропіти
end unless not tired # якщо не не втомлений
Стислість такого запису корисна, коли, приміром, вам потрібно швидко виконати добре визначену дію, якщо умова є істинною.
Ось, як ви можете додати у ваш код зневаджувальний вивід, якщо константа DEBUG
має істинне значення:
puts("somevar = #{somevar}") if DEBUG
Оператор case
Коли вам потрібно виконати ряд різних дій, в залежності від значення однієї змінної, декілька перевірок if..elsif
є занадто багатослівними і містять багато повторень. Елегантне альтернативне рішення пропонує оператор case
. Він починається зі слова case
, після якого йде назва змінної для перевірки. Після цього йде ряд секцій when
, кожна з яких визначає пускове значення для коду, який йде після неї. Цей код виконується, лише коли значення змінної, яка перевіряється, дорівнює пусковому значенню:
case(i)
when 1 : puts("Понеділок")
when 2 : puts("Вівторок")
when 3 : puts("Середа")
when 4 : puts("Четвер")
when 5 : puts("П’ятниця")
when (6..7) : puts("Юху! Вихідні!")
else puts("Такого дня не існує!")
end
У цьому прикладі я використовую двокрапку, щоб відділити перевірку when
від коду, який має виконуватись. Ви також можете використовувати ключове слово then
:
when 1 then puts("Понеділок")
Двокрапка або then
можна упустити, якщо перевірка та код, який виконається, розміщені на різних рядках. На відміну від оператора case
у C-подібних мовах, немає потреби вставляти ключове слово break
для того, що перервати перехід виконання до секцій, які знаходяться нижче.
В Ruby, як тільки знайдене співпадіння, оператор case
завершує роботу:
case(i)
when 5 : puts("П’ятниця")
puts("...майже вихідні!")
when 6 : puts("Субота!")
# the following never executes
when 5 : puts("А тепер знову п’ятниця!")
end
Ви можете декілька рядків коду між кожною умовою when
, і ви можете перелічити декілька значень розділених комами, щоб запустити певний блок when
, ось так:
when 6, 7 : puts("Юху! Вихідні!")
Умова в операторі case
не обов’язково має бути простою змінною, це може бути і вираз, як от:
case(i + 1)
Ви також можете використовувати нецілочисельні типи, як от рядки.
Декілька пускових значень визначених у секції when
можуть мати різні типи – наприклад, і рядки, і цілі числа:
when 1, 'Monday', 'Mon' : puts("Так, '#{i}' це понеділок")
Ось розгорнутіший приклад, який ілюструє синтаксичні елементи, про які йшлось раніше:
case(i)
when 1 : puts("Понеділок")
when 2 : puts("Вівторок")
when 3 : puts("Середа")
when 4 : puts("Четвер")
when 5 then puts("П’ятниця")
puts("...майже вихідні!")
when 6, 7
puts("Субота!") if i == 6
puts("Неділя!") if i == 7
puts("Юху! Вихідні!")
# все, що нижче ніколи не виконається
when 5 : puts("А тепер знову п’ятниця!")
else puts("Такого дня не існує!")
end