Шаблоны в switch
В прошлом уроке над каждым жёлобом сортировочной машины висела табличка с номерами или диапазоном. Иногда этого мало: нужен жёлоб «все числа меньше нуля», а перечислить их нельзя. Тогда у жёлоба ставят контролёра: он смотрит на посылку и решает, пускать ли её внутрь.
Табличку после case называют шаблоном: значение либо подходит под него, либо нет. Список значений и диапазон — уже знакомые шаблоны. Вот ветка с контролёром:
let morning = -9
let noon = 1
let average = (morning + noon) / 2
switch average {
case let t where t < 0:
print("frost \(t)") // frost -4
case 0...15:
print("cool")
default:
print("warm")
}Давайте пройдём по программе:
averageполучает(-9 + 1) / 2, то есть-4.case let t where t < 0:кладёт-4в новую константуtи проверяет условиеt < 0.- Условие истинно, поэтому выполняется эта ветка: программа печатает
frost -4. - Ветки
0...15иdefaultпрограмма пропускает.
Константа в шаблоне и where
Ветка case let t where t < 0: состоит из двух частей.
case let
Шаблон let t подходит под любое значение и кладёт его в новую константу t. Эта константа видна только внутри своей ветки. Там с ней работают как с любой другой константой: в примере она попадает в строку через \(t).
where
После where пишут условие типа Bool. Ветка выполняется, только если шаблон подошёл и условие истинно. Если условие ложно, switch идёт к следующей ветке, как будто шаблон не подошёл.
Это и есть контролёр у жёлоба: табличка let t пускает любую посылку, а where t < 0 пускает внутрь только отрицательные числа.
switch по строке
switch работает не только с числами. Ветки могут сравнивать строку с заранее записанными строками:
let command = "stop"
switch command {
case "start":
print("starting")
case "stop", "pause":
print("stopping") // stopping
default:
print("unknown command")
}Что важно знать о таком switch:
- строка сравнивается целиком и с учётом регистра:
"Stop"подcase "stop"не подойдёт; - несколько строк в одной ветке перечисляют через запятую, как числа;
- строк бесконечно много, поэтому
switchпоStringтоже требуетdefault.
fallthrough
Иногда выполнение должно намеренно провалиться в следующую ветку. Тогда в конце ветки пишут слово fallthrough:
let role = "editor"
switch role {
case "admin":
print("delete posts")
fallthrough
case "editor":
print("edit posts") // edit posts
fallthrough
default:
print("read posts") // read posts
}Разберём, как работает fallthrough:
fallthroughпереходит в начало следующей ветки и не проверяет её шаблон: приrole == "editor"программа печатаетedit posts, а затемread postsизdefault;- при
"admin"программа напечатала бы все три строки, а при любой другой роли — толькоread posts; - в последней ветке
fallthroughписать нельзя: переходить некуда.
Нужен fallthrough редко: чаще хватает нескольких значений в одной ветке.
Частая ошибка
Две ветки с where вместе покрывают все числа, но default не написали:
switch average { // ✗
case let t where t < 0:
print("below zero")
case let t where t >= 0:
print("zero or above")
}error: switch must be exhaustiveЧеловек видит, что любое число либо меньше нуля, либо нет. А компилятор условия после where между собой не сравнивает и считает, что каждая такая ветка может не подойти.
Здесь картинка с контролёрами неточна: настоящие контролёры могли бы договориться между собой, а условие where знает только о своей ветке. Поэтому последнюю ветку пишут как default: она примет всё, что не подошло под ветки выше.
Главное
- Шаблон
let tвcaseкладёт значение в новую константу, которая видна только внутри ветки. whereдобавляет к ветке условие; компилятор не сравнивает такие условия между собой, поэтому ветки сwhereне заменяютdefault.switchпо строке сравнивает её целиком, с учётом регистра, и тоже требуетdefault.fallthroughпереходит в следующую ветку, не проверяя её шаблон.