JavaRush /Курси /Swift SELF /Логічні операції &&<...

Логічні операції &&, ||, ! та таблиці істинності

Swift SELF
Рівень 3 , Лекція 3
Відкрита

1. Вступ

Уявіть, що ви пишете програму-«вахтера» для бібліотеки. Так, знову бібліотека: програмісти люблять бібліотеки, бо там тихо й ніхто не відволікає від налагодження. Вахтер має вирішити, чи може відвідувач зайти всередину і чи можна йому брати книги. Одного порівняння на кшталт age >= 18 майже ніколи не вистачає. Зазвичай потрібні кілька умов одразу: «є читацький квиток», «немає заборгованості», «або дорослий, або прийшов із дорослим». Саме тут логічні оператори стають вашим швейцарським ножем.

Важливо зрозуміти базову ідею: порівняння та перевірки дають нам маленькі шматочки типу Bool (тобто true/false), а &&, ||, ! дозволяють скласти з цих шматочків складніший зміст — майже як із кубиків LEGO, тільки без ризику наступити на них уночі.

2. Оператор &&: логічне «І»

Коли ви читаєте умову A && B, це звучить як «A і B». Але в програмуванні «і» має точне значення: результат буде true лише тоді, коли обидві частини — true. Якщо хоча б одна частина false, увесь вираз стає false. Тут логіка залізобетонна: «пройти можна, якщо є квиток і немає боргів». Якщо квиток є, але борги теж є, бібліотека, на жаль, сьогодні сувора.

У Swift && використовують для об’єднання двох виразів типу Bool. Зазвичай кожну частину ви отримуєте через порівняння, наприклад age >= 18, або через уже готову булеву змінну, наприклад hasCard.

Міні-приклад: «у діапазоні від 1 до 10»

let x = Int(readLine() ?? "") ?? 0
let isInRange = x >= 1 && x <= 10

print("isInRange = \(isInRange)") // наприклад: isInRange = true

Тут дві перевірки працюють разом: число має бути і не меншим за 1, і не більшим за 10.

Таблиця істинності для &&

Щоб не вгадувати результат, логіка любить таблиці істинності. Це проста таблиця, де ми перебираємо всі варіанти true/false.

A B A && B
false
false
false
false
true
false
true
false
false
true
true
true

Ця таблиця корисна тим, що допомагає перевірити власні міркування. Якщо вам здається, що true && false має бути true, ні — це просто ваш мозок утомився.

3. Оператор ||: логічне «АБО»

Оператор || читається як «або», але не як розмовне «або… або…», де інколи мають на увазі вибір лише одного варіанта. У програмуванні A || B означає: «A істинне або B істинне, або обидва істинні». Результат буде false лише тоді, коли обидві частини false.

Це зручно, коли підходить кілька варіантів. Наприклад: «вхід дозволено, якщо сьогодні субота або неділя», або «команда допустима, якщо це help або exit».

Міні-приклад: «команда — одна з дозволених»

let command = readLine() ?? ""
let isServiceCommand = command == "help" || command == "exit"

print("isServiceCommand = \(isServiceCommand)") // наприклад: true

Таблиця істинності для ||

A B A || B
false
false
false
false
true
true
true
false
true
true
true
true

Найважливіший висновок: || дає false лише в одному випадку — коли все погано одразу.

4. Оператор !: логічне «НЕ»

Іноді ви вже обчислили умову, але вам потрібен протилежний результат. Наприклад, у вас є hasDebt («є борг»), а перевірити треба «боргу немає». Можна, звісно, писати hasDebt == false, але це читається тяжкувато: «є борг дорівнює хибність» — звучить радше як філософія, а не код. Для цього й існує оператор заперечення !.

!A означає «не A». Якщо A було true, стане false. Якщо було false, стане true. Дуже прямо — як охоронець у бібліотеці.

Міні-приклад: «немає заборгованості»

let debtText = readLine() ?? ""
let hasDebt = debtText == "yes"
let hasNoDebt = !hasDebt

print("hasDebt = \(hasDebt)")       // наприклад: hasDebt = true
print("hasNoDebt = \(hasNoDebt)")   // наприклад: hasNoDebt = false

Таблиця істинності для !

A !A
false
true
true
false

5. Склеюємо умови: спочатку «цеглинки», потім «будинок»

Перш ніж перейти до важливої особливості && і ||, варто обговорити стиль, який справді береже нерви. Новачки часто намагаються одразу писати гігантські умови прямо в if: в один рядок, із п’ятьма порівняннями та двома запереченнями. Компілюється це без проблем, але читати потім важко навіть через кілька днів.

Гарний прийом — спочатку обчислити маленькі булеві «цеглинки» в let, а потім зібрати з них підсумкову умову. Це не лише питання «красивості», а й спосіб не переплутати зміст.

Міні-приклад: перевірка допуску до бібліотеки

let age = Int(readLine() ?? "") ?? 0
let cardText = readLine() ?? ""         // очікуємо "yes" або "no"
let debtText = readLine() ?? ""         // очікуємо "yes" або "no"

let isAdult = age >= 18
let hasCard = cardText == "yes"
let hasDebt = debtText == "yes"

let canEnter = isAdult && hasCard && !hasDebt
print("canEnter = \(canEnter)") // наприклад: canEnter = true

Зверніть увагу: тут ми вже використали && кілька разів поспіль — це нормально, бо всі частини мають один тип, а зміст читається як «дорослий, із квитком і без боргів».

6. Коротка перевірка: short-circuit у && і ||

Зараз буде момент, який робить && і || не просто логічними операторами, а ще й інструментом безпеки. У Swift, як і в багатьох мовах, && і || працюють із короткою перевіркою: інколи права частина взагалі не обчислюється, бо відповідь уже видно з лівої. Це важливо не лише для швидкості, а й для того, щоб не виконувати небезпечні дії на кшталт ділення на нуль.

Спочатку вираз читається й обчислюється зліва направо, і Swift намагається зберігати саме такий порядок.
Для && це означає: якщо зліва вже false, то весь вираз не може стати true, отже праворуч навіть не треба дивитися. Для || навпаки: якщо зліва вже true, то весь вираз уже true, і перевіряти праворуч немає сенсу.

У документації та пропозиціях щодо розвитку Swift це підкреслюють навіть опосередковано — зокрема там, де говорять про схожі, але не такі самі логічні операції. Наприклад, у пропозиції щодо SIMD-масок прямо сказано, що спеціальні булеві операції на масках не short-circuit, «на відміну від && і ||».

Short-circuit у &&: «запобіжник» ставимо ліворуч

Найкласичніший «запобіжник» — захист від ділення на нуль. У Swift ділення на нуль призводить до аварійного завершення програми (runtime error). Тому, якщо праворуч у виразі є потенційно небезпечна операція, спочатку зліва перевіряємо, що вона безпечна.

let a = Int(readLine() ?? "") ?? 0
let b = Int(readLine() ?? "") ?? 0

let isSafeAndBig = a != 0 && (b / a) > 2
print("isSafeAndBig = \(isSafeAndBig)") // за a == 0 програма НЕ впаде

Чому не впаде? Тому що при a == 0 ліва частина a != 0 дає false, а отже false && що завгодно все одно false. І Swift навіть не полізе обчислювати (b / a) > 2.

Запам’ятайте це як практичне правило: якщо праворуч є небезпечна частина, у && ліворуч має стояти запобіжник.

Short-circuit у ||: «швидкий успіх» ставимо ліворуч

У || логіка дзеркальна. Якщо ви вже знаєте, що умова істинна, немає сенсу перевіряти другий варіант. На практиці це корисно, коли праворуч у вас важча перевірка, наприклад довге порівняння або потенційно ризикований доступ.

Ми поки не вміємо красиво показати «важку операцію» без функцій і замикань; до них дійдемо значно пізніше. Але зміст можна прочитати так: якщо перший варіант підходить, другий уже не потрібен. Це і є коротка перевірка, яка в || працює тому, що true || що завгодно завжди true.

7. Приклад: «Вахтер бібліотеки»

Давайте акуратно з’єднаємо &&, ||, ! в один невеликий сценарій. Поки без циклів — вони будуть завтра, — тому програма «одноразова»: зчитала введення, ухвалила рішення, вивела результат. Але навіть так вона вже схожа на справжню валідацію.

Умови такі, спеціально життєві, щоб мозок не нудьгував:

  • Увійти до бібліотеки можна, якщо є читацький квиток і немає заборгованості.
  • Взяти книгу можна, якщо можна увійти, і при цьому або ви дорослий, або прийшли з дорослим супроводжуючим.

Ми спеціально не будемо змішувати && і || в одному величезному рядку — натомість винесемо проміжну умову в окремий let. Так і читається простіше, і менше шансів заплутатися. Про змішування та дужки поговоримо в наступній лекції — там буде окрема магія змісту.

let age = Int(readLine() ?? "") ?? 0
let cardText = readLine() ?? ""         // очікуємо "yes" або "no"
let debtText = readLine() ?? ""         // очікуємо "yes" або "no"
let adultWithYouText = readLine() ?? "" // очікуємо "yes" або "no"

let hasCard = cardText == "yes"
let hasDebt = debtText == "yes"
let isAdult = age >= 18
let hasAdultWithYou = adultWithYouText == "yes"

let canEnter = hasCard && !hasDebt
let isAdultOrWithAdult = isAdult || hasAdultWithYou
let canBorrow = canEnter && isAdultOrWithAdult

print("canEnter = \(canEnter)")   // наприклад: canEnter = true
print("canBorrow = \(canBorrow)") // наприклад: canBorrow = false

Зверніть увагу на !hasDebt. У реальному коді таке трапляється постійно: ми зберігаємо факт «є борг», але перевіряємо «боргу немає». І ! робить це максимально коротко й чесно.

Якщо вам хочеться запитати: «А чому ми просимо вводити саме yes і no, а не “Так/Ні” чи “YES”?» — тому що нормалізація введення користувача, тобто обрізання пробілів і приведення регістру, буде пізніше. Зараз наша мета — логіка, а не боротьба з людським фактором.

Таблиці істинності як інструмент налагодження

Таблиця істинності звучить трохи нудно, але на практиці це майже міні-налагоджувач для голови. Коли ви пишете складну умову, ви фактично будуєте маленьку логічну схему. І якщо схема неправильна, програма «правильно» робитиме неправильні речі, а це найпідступніший тип помилок.

Корисний спосіб самоперевірки такий: берете ваші булеві цеглинки і подумки проганяєте 24 характерні сценарії. Наприклад, для нашої бібліотеки:

  • hasCard = false, hasDebt = false → вхід? Ні, бо немає квитка.
  • hasCard = true, hasDebt = true → вхід? Ні, бо є борг.
  • canEnter = true, isAdult = false, hasAdultWithYou = true → взяти книгу? Так, бо поруч є дорослий.
  • canEnter = true, isAdult = false, hasAdultWithYou = false → взяти книгу? Ні.

І якщо десь ваша відповідь за змістом не збігається з тим, що дає формула з &&, ||, !, значить, формула описує не ті правила, які ви хотіли закласти.

8. Типові помилки під час роботи з &&, ||, !

Помилка №1: плутати людське “або” з програмним ||.
У розмовній мові «чай або кава» інколи означає «оберіть щось одне». У програмуванні A || B означає «достатньо одного істинного», і якщо істинні обидва — це теж нормально. Через це умова може виявитися надто «дозвільною», і програма почне пропускати випадки, які ви не планували.

Помилка №2: забувати, що ! стосується одного булевого значення.
Сам по собі ! перевертає один Bool. Новачки інколи думають, що ! «скасовує» весь умовний вираз навколо, хоча насправді він застосовується до конкретного шматочка. Якщо ви хотіли заперечити результат усієї перевірки, краще спочатку зберегти її в змінну, а потім написати let isNotAllowed = !isAllowed. Так зміст буде видно одразу, і ви не будете сперечатися із самим собою про те, що саме заперечується.

Помилка №3: ставити «запобіжник» праворуч у &&.
Якщо праворуч у вас потенційно небезпечний вираз, наприклад ділення, остача або доступ до чогось, а перевірку безпеки ви ставите після нього, short-circuit не допоможе. У && безпечна перевірка має стояти ліворуч, тому що права частина може й не виконатися, а ліва виконується завжди.

Помилка №4: перетворювати умову на «простирадло», яке неможливо прочитати.
Навіть якщо умова формально правильна, вона може бути непридатною для читання. Коли ви бачите в if десять операторів підряд, мозок починає вдавати, що це не його проблема. Ліки просте: робіть let isAdult = …, let hasCard = …, let canEnter = …. Це не «зайві рядки», а спосіб зробити код зрозумілим.

Помилка №5: намагатися використовувати &&/|| не з Bool.
У Swift умова має бути саме Bool, і оператори &&/|| очікують булеві значення ліворуч і праворуч. Не можна писати «0 означає false, 1 означає true» як у деяких інших мовах. Тому завжди спочатку робіть порівняння (x > 0, command == "exit"), а вже потім об’єднуйте результати логікою.

1
Задача
Swift SELF, 3 рівень, 3 лекція
Недоступна
Сервісний режим
Сервісний режим
1
Задача
Swift SELF, 3 рівень, 3 лекція
Недоступна
Пропуск за правилами
Пропуск за правилами
1
Задача
Swift SELF, 3 рівень, 3 лекція
Недоступна
Пільга в касі
Пільга в касі
1
Задача
Swift SELF, 3 рівень, 3 лекція
Недоступна
Перевірка подільності
Перевірка подільності
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ