Перейти к содержимому
Теория · Операторы · урок 6 из 7

Диапазоны и тернарный оператор

Детский билет в музей продают детям от 3 до 12 лет. Двенадцатилетний его получит: граница входит в отрезок. Магазин работает с 9:00 до 18:00, но в 18:00 двери уже закрыты: вторая граница — это момент, когда отрезок кончается.

Для каждого из двух случаев в Swift есть свой оператор. Оба создают диапазон — отрезок чисел от нижней границы до верхней.

Закрытый диапазон ...

Запись a...b включает обе границы, как детский билет:

SWIFT
let childAges = 3...12
print(childAges.contains(12)) // true
print(childAges.contains(13)) // false

Разберём по строкам:

  1. 3...12 — все целые числа от 3 до 12, вместе с 12.
  2. contains проверяет, попадает ли число в диапазон. Это действие умеет выполнять сам диапазон, и его вызывают через точку: childAges.contains(12) даёт true.
  3. Число 13 в диапазон не входит, и contains(13) даёт false.

Нижняя граница не может быть больше верхней. Запись 12...3 не даёт пустой диапазон, а аварийно завершает программу:

TEXT
Fatal error: Range requires lowerBound <= upperBound

Полуоткрытый диапазон ..<

Запись a..<b останавливается перед b, как часы работы магазина:

SWIFT
let openHours = 9..<18
print(openHours.contains(9)) // true
print(openHours.contains(18)) // false

Нижняя граница 9 входит в диапазон, а верхняя 18 — нет. Картинка с часами здесь неточна: в 9..<18 лежат только целые числа от 9 до 17, а у настоящего времени есть ещё и минуты.

Полуоткрытый диапазон удобен, когда верхняя граница означает конец или количество: в 0..<5 ровно пять чисел, от 0 до 4.

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

Скобки вокруг диапазона

Чтобы вызвать contains прямо у записи диапазона, её берут в скобки: (1...5).contains(5). Без скобок Swift применит .contains к одному числу 5:

TEXT
error: value of type 'Int' has no member 'contains'

Тернарный оператор

Часто нужно выбрать одно из двух значений: детский билет или взрослый. Для этого есть тернарный оператор: условие ? a : b. Его название значит «из трёх частей»: условие и два значения.

SWIFT
let age = 7
let ticket = childAges.contains(age) ? "child" : "adult"
print(ticket) // child

Как Swift выбирает значение:

  1. Сначала он вычисляет условие перед ?: childAges.contains(age) — это true.
  2. Условие верно, поэтому результатом становится значение между ? и :, строка "child".
  3. Если бы условие было ложным, результатом стало бы значение после :, строка "adult".

У тернарного оператора два правила:

  • условие перед ? имеет тип Bool;
  • оба значения одного типа, иначе программа не скомпилируется.

Если смешать строку и число, например age > 5 ? "big" : 0, компилятор ответит так:

TEXT
error: result values in '? :' expression have mismatching types 'String' and 'Int'

Частая ошибка

Часы работы магазина записали закрытым диапазоном:

SWIFT
let hours = 9...18 // ✗
print(hours.contains(18)) // true

Программа считает магазин открытым в 18:00, хотя в это время он уже закрыт. Ошибки нет ни при компиляции, ни при запуске, есть только неверный ответ. Когда верхняя граница означает конец, нужен ..<, как в openHours выше.

Главное

  • a...b включает обе границы: (3...12).contains(12) — это true.
  • a..<b останавливается перед b: (9..<18).contains(18) — это false.
  • contains проверяет, попадает ли число в диапазон, а запись диапазона перед .contains берут в скобки.
  • условие ? a : b даёт a, если условие верно, и b, если нет; оба значения одного типа.