1. Мені потрібне число
Коли ви розвʼязуєте консольні задачі, введені дані майже завжди мають вигляд тексту: навіть якщо користувач вводить 42, програма спочатку отримує рядок "42". І тут виникає класична ситуація: за змістом це число, бо ми хочемо додавати, ділити й порівнювати, а фактично — текст. Якщо не виконати перетворення, програма не рахуватиме, а поводитиметься з рядками: склеюватиме їх, друкуватиме й підсумовуватиме.
Уявіть, що ви замовили в кафе дві кави, а вам принесли папірець із написом 2. Технічно це «два», але випити це неможливо. Перетворення рядка на число — це наш спосіб перетворити напис на реальну кількість кави.
Рядок — це ще не число
let text = "10"
print(text + text) // 1010
Пояснення: це не «двадцять», а просто склеювання "10" і "10". Щоб отримати 20, треба перетворити значення на число.
2. Знайомство з Int() і Double()
Swift — сувора мова: вона не виконує неявних перетворень, щоб випадково не накоїти лиха. Якщо у вас є рядок, а ви хочете отримати з нього число, вам потрібно написати Int(string). Або Double(string), якщо рядок містить дробове число. Приклад:
let five = "5"
let two = "2.0"
let a = Int(five) // Int?
let b = Double(two) // Double?
// let sum = a + b // ❌ помилка: різні типи
let sum = Double(a) + b // ✅ спочатку перетворюємо Int на Double
print(sum) // 7.0
Спочатку ми перетворили рядки five і two на числа: ціле й дробове. Потім додали їх. Довелося попередньо перетворити ціле на дробове, тому що Swift не любить додавати числа з різними типами.
Чому це важливо саме для введення
Користувач вводить текст, але за змістом ви заздалегідь знаєте, що очікуєте:
- «вік» → Int
- «ціна» → Double
Отже, далі ви перетворюватимете рядок на потрібний тип. Але тут є пастка: рядок може виявитися неправильним. Тому Swift не гарантує успіху — і повертає Optional. Саме до цього ми й переходимо.
3. Int(...) і Double(...) повертають Optional
Тепер головне відкриття лекції: у Swift перетворення рядка на число може не вдатися, тому результат — опційний.
Тобто:
- Int("123") повертає Int?
- Double("3.14") повертає Double?
Чому так? Тому що рядок може бути нечисловим: "abc", "12a", "3,14" (якщо очікується крапка), порожній рядок "". Swift не хоче вдавати, що все добре. Він чесно каже: «Я спробував. Якщо вийшло — тримай число. Якщо ні — тримай nil».
Успішне перетворення
let value = Int("42")
print(value) // Optional(42)
Так, print() покаже Optional(42) — це нормально: «у мене не просто число, а коробочка, в якій може бути число».
Невдале перетворення
let value = Int("cat")
print(value) // nil
4. Де в ланцюжку «ввід → число» зʼявляється nil
Тут легко заплутатися, тому розкладемо по поличках. У нашому ланцюжку є дві окремі точки, де все може стати nil.
Перша — readLine() може повернути nil, якщо введення відсутнє. Друга — навіть якщо рядок є, Int(line) може повернути nil, якщо рядок не схожий на число.
Корисно тримати в голові просту схему:
flowchart TD
A["readLine()"] --> B{Рядок є?}
B -- ні --> N1[nil]
B -- так --> C["Int(line) / Double(line)"]
C --> D{Це число?}
D -- ні --> N2[nil]
D -- так --> OK[Число]
Саме тому в задачах часто радять спочатку підстрахуватися від nil, а потім ще раз перевірити результат.
5. Оператор ??: «якщо nil — візьми запасний варіант»
Тепер наш найкращий друг на найближчі задачі — оператор ?? (nil-coalescing operator). Він означає:
«Якщо зліва nil, візьми те, що справа. Якщо зліва не nil, візьми ліве».
Важливий момент: ?? застосовується, коли зліва Optional. У сучасному Swift це суворо: якщо зліва не Optional, використовувати ?? не можна — і це добре, бо інакше можна випадково приховати помилку.
Приклад: підставляємо значення за замовчуванням для String?
let line: String? = nil
let safeLine = line ?? "0"
print(safeLine) // 0
safeLine — уже звичайний рядок String, не опціональний.
Приклад: підставляємо значення за замовчуванням для Int?
let parsed: Int? = Int("not a number")
let value = parsed ?? 0
print(value) // 0
6. Патерн: введення → розбір → значення за замовчуванням
Зберемо все в один практичний рецепт. Він трохи довший, зате передбачуваний і добре підходить новачкам. Та й менторам теж — особливо коли код не падає на перевірці.
Варіант А: компактно (в один рядок)
let number = Int(readLine() ?? "") ?? 0
print(number)
Тут відбуваються одразу дві підстраховки:
Спочатку readLine() ?? "" гарантує рядок, навіть порожній, а потім Int(...) ?? 0 гарантує число.
Код робочий, але в ньому легко заплутатися через велику кількість дужок.
Варіант B: читабельно (кілька кроків)
let line = readLine() // String?
let safeLine = line ?? "" // String
let number = Int(safeLine) ?? 0 // Int
print(number)
Цей стиль часто кращий для навчання: ви бачите, де саме був Optional і де саме від нього позбулися.
7. Корисні нюанси
Int vs Double: ціла кількість і дробова частина
У межах цього рівня нам не потрібно заглиблюватися в точність і порівняння Double (це буде на іншому рівні), але потрібно чітко розуміти смислову різницю.
- Int — для «штук»: кількість книг, вік, число студентів, коробки, лайки.
- Double — коли можлива дробова частина: ціна 19.99, вага 2.5, середнє значення 3.75.
І так, гроші — тонка тема, але в межах цього рівня ми використовуємо Double просто як «число з крапкою».
Уявляйте це як вибір контейнера: якщо ви рахуєте яблука, беріть коробку для цілих яблук (Int). Якщо ви рахуєте сік у літрах, беріть мірний стакан (Double).
Приклад: парсимо Double зі значенням за замовчуванням
let price = Double(readLine() ?? "") ?? 0.0
print(price)
Чому значення за замовчуванням 0.0, а не 0? Тому що тип має збігатися: праворуч від ?? має бути значення того самого типу, який ви хочете отримати.
Памʼятка: які типи повертаються
Іноді мозок намагається запамʼятати це як заклинання, але краще подивитися на таблицю.
| Вираз | Тип результату | Чому |
|---|---|---|
|
|
введення може бути відсутнім |
|
|
?? підставив значення за замовчуванням |
|
|
рядок може не містити числа |
|
|
?? підставив число за замовчуванням |
|
|
рядок може не містити числа |
|
|
значення за замовчуванням для Double |
8. Мініпроєкт: калькулятор чайових
Далі продовжимо ідею: ми збираємо один і той самий застосунок частинами. Сьогодні це буде дуже простий консольний помічник: користувач вводить суму рахунку та відсоток чайових, а програма рахує підсумок.
Ми поки що не вміємо красиво обробляти неправильне введення (це знадобиться пізніше), тож діємо прагматично: якщо введення неправильне, використовуємо значення за замовчуванням.
Крок 1: читаємо суму рахунку як Double
let billText = readLine() ?? ""
let bill = Double(billText) ?? 0.0
print("Рахунок: \(bill)")
Якщо введуть 120.5 — чудово. Якщо введуть «котик» — рахунок стане 0.0. Неприємно для ресторану, але приємно для компілятора.
Крок 2: читаємо відсоток чайових як Int і переводимо в частку
Відсоток зручно вводити цілим числом: 10, 15, 20.
let tipPercentText = readLine() ?? ""
let tipPercent = Int(tipPercentText) ?? 0
let tipRate = Double(tipPercent) / 100.0
print("Чайові: \(tipPercent)%")
Зверніть увагу: ми перетворюємо Int на Double через Double(tipPercent), тому що інакше ділення було б цілочисельним, а нам потрібен дріб.
Крок 3: рахуємо підсумок
let billText = readLine() ?? ""
let bill = Double(billText) ?? 0.0
let tipPercentText = readLine() ?? ""
let tipPercent = Int(tipPercentText) ?? 0
let tipRate = Double(tipPercent) / 100.0
let total = bill + bill * tipRate
print("Разом: \(total)")
Код вийшов трохи довгим, але це усвідомлена плата за зрозумілість: ви бачите кожен крок, і вам простіше налагоджувати.
9. Типові помилки
Помилка №1: «Я впевнений, що на вході число, тож пишу так, ніби Optional не існує».
Зазвичай це виглядає як спроба одразу працювати з readLine() як із рядком або з Int(readLine()) як із числом. Компілятор не дасть зробити це «нишком», і це його спосіб урятувати вас від падіння програми. Звикайте: якщо бачите ? у типі — це червоний прапорець: значення може бути відсутнім.
Помилка №2: підстрахували readLine(), але забули, що Int(...) теж може повернути nil.
Дуже частий сценарій: людина пише let line = readLine() ?? "0", а потім робить let x = Int(line) і думає, що x — це Int. Але x усе ще Int?. Тому «подвійна підстраховка» (?? після readLine() і ?? після Int(...)) — не параноя, а реальність.
Помилка №3: неправильне значення за замовчуванням за типом (0 замість 0.0 або навпаки).
Int(...) ?? 0.0 не скомпілюється, і це правильно: праворуч має бути Int. Аналогічно Double(...) ?? 0 іноді не проходить перевірку типів за строгих перевірок. Звикайте обирати значення за замовчуванням «у стилі типу»: для Int — 0, для Double — 0.0.
Помилка №4: спроба розібрати дріб через Int.
Якщо користувач вводить 3.14, а ви робите Int("3.14"), отримаєте nil і раптово перейдете до значення за замовчуванням. Якщо за змістом можлива дробова частина — використовуйте Double. Якщо за змістом має бути ціле число — використовуйте Int і не сподівайтеся, що 3.0 «якось само» стане цілим.
Помилка №5: «магічний рядок» у значенні за замовчуванням, який ламає логіку.
Іноді хочеться написати readLine() ?? "1" «бо так менше шансів на ділення на нуль». Але це робить програму дивною: відсутність введення перетворюється на конкретне значення і спотворює результат. Значення за замовчуванням треба обирати свідомо: або 0 як «нічого не ввели», або таке значення, яке за змістом безпечніше саме у вашій задачі, і ви можете це пояснити.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ