Unetway

Swift - Контроль потока

Swift предоставляет множество операторов потока управления. Они включают в себя whileциклы для выполнения задачи несколько раз; ifguardИ switchзаявление выполнять различные ветви коды , основанной на определенных условиях; и такие операторы, как breakи, continueчтобы перенести поток выполнения в другую точку вашего кода.

Swift также обеспечивает forinцикл , который позволяет легко перебирать массивы, словари, диапазоны, строку и другие последовательности.

switchУтверждение Свифта значительно мощнее, чем его аналог во многих С-подобных языках. Случаи могут соответствовать множеству различных шаблонов, включая совпадения интервалов, кортежи и приведение к определенному типу. Совпадающие значения в switchслучае могут быть связаны с временными константами или переменными для использования в теле случая, и сложные условия соответствия могут быть выражены с помощью whereпредложения для каждого случая.

Петли для

Вы используете цикл forinдля итерации последовательности, такой как элементы в массиве, диапазоны чисел или символы в строке.

В этом примере используется цикл forinдля перебора элементов в массиве:

let names = ["Anna", "Alex", "Brian", "Jack"]

for name in names {

print("Hello, \(name)!")

}

// Hello, Anna!

// Hello, Alex!

// Hello, Brian!

// Hello, Jack!

Вы также можете перебирать словарь для доступа к его парам ключ-значение. Каждый элемент в словаре возвращается как кортеж, когда словарь повторяется, и вы можете разложить члены кортежа как явно названные константы для использования в теле цикла - . В приведенном ниже примере кода ключи словаря раскладываются в константу с именем , а значения словаря раскладываются в константу с именем .(key, value)(key, value)forinanimalNamelegCount

let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]

for (animalName, legCount) in numberOfLegs {

print("\(animalName)s have \(legCount) legs")

}

// cats have 4 legs

// ants have 6 legs

// spiders have 8 legs

Содержимое a Dictionaryпо своей природе неупорядочено, и итерации по ним не гарантируют порядок, в котором они будут получены. В частности, порядок вставки элементов в Dictionaryне определяет порядок их итерации. Для получения дополнительной информации о массивах и словарях см. Типы коллекций .

Вы также можете использовать forinциклы с числовыми диапазонами. В этом примере печатаются первые несколько записей в пятикратной таблице:

for index in 1...5 {

print("\(index) times 5 is \(index * 5)")

}

// 1 times 5 is 5

// 2 times 5 is 10

// 3 times 5 is 15

// 4 times 5 is 20

// 5 times 5 is 25

Итерируемая последовательность представляет собой диапазон чисел от 1до 5, включительно, на что указывает использование оператора закрытого диапазона ( ...). Значение indexустанавливается в первое число в диапазоне ( 1), и операторы внутри цикла выполняются. В этом случае цикл содержит только одну инструкцию, которая печатает запись из пятикратной таблицы для текущего значения index. После выполнения оператора значение indexобновляется, чтобы содержать второе значение в диапазоне ( 2), и print(_:separator:terminator:)функция вызывается снова. Этот процесс продолжается, пока не будет достигнут конец диапазона.

В приведенном выше примере indexэто константа, значение которой автоматически устанавливается в начале каждой итерации цикла. Как таковой, indexне должен быть объявлен, прежде чем он будет использован. Он неявно объявляется просто путем включения его в объявление цикла без необходимости использования letключевого слова объявления.

Если вам не нужно каждое значение из последовательности, вы можете игнорировать значения, используя подчеркивание вместо имени переменной.

let base = 3

let power = 10

var answer = 1

for _ in 1...power {

answer *= base

}

print("\(base) to the power of \(power) is \(answer)")

// Prints "3 to the power of 10 is 59049"

В приведенном выше примере вычисляется значение одного числа в степени другого (в данном случае 3в степени 10). Он умножает начальное значение 1(то есть 3на степень 0) в 3десять раз, используя закрытый диапазон, который начинается с 1и заканчивается на 10. Для этого расчета отдельные значения счетчика каждый раз в цикле не нужны - код просто выполняет цикл правильное число раз. Символ подчеркивания ( _), используемый вместо переменной цикла, вызывает игнорирование отдельных значений и не обеспечивает доступ к текущему значению во время каждой итерации цикла.

В некоторых ситуациях вы можете не захотеть использовать закрытые диапазоны, которые включают обе конечные точки. Подумайте о том, чтобы наносить отметки за каждую минуту на циферблате. Вы хотите рисовать 60отметки, начиная с 0минуты. Используйте оператор полуоткрытого диапазона ( ..<), чтобы включить нижнюю границу, но не верхнюю границу. Для получения дополнительной информации о диапазонах см. Операторы диапазона .

let minutes = 60

for tickMark in 0..<minutes {

// render the tick mark each minute (60 times)

}

Некоторым пользователям может потребоваться меньше отметок в их интерфейсе. 5Вместо этого они могли бы предпочесть одну отметку каждую минуту. Используйте stride(from:to:by:)функцию, чтобы пропустить нежелательные метки.

let minuteInterval = 5

for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {

// render the tick mark every 5 minutes (0, 5, 10, 15 ... 45, 50, 55)

}

Закрытые диапазоны также доступны, используя stride(from:through:by:)вместо этого:

let hours = 12

let hourInterval = 3

for tickMark in stride(from: 3, through: hours, by: hourInterval) {

// render the tick mark every 3 hours (3, 6, 9, 12)

}

Пока петли

whileЦикл выполняет набор операторов , пока условие не станет false. Такие циклы лучше всего использовать, когда число итераций неизвестно до начала первой итерации. Swift предоставляет два вида whileпетель:

  • while оценивает его состояние в начале каждого прохода через цикл.
  • repeatwhileоценивает его состояние в конце каждого прохода через цикл.

В то время как

whileЦикл начинается с вычисления одно условие. Если условие выполнено true, набор инструкций повторяется до тех пор, пока условие не станет false.

Вот общая форма whileцикла:

while condition {

statements

}

Этот пример играет в простую игру « Змеи и лестницы» (также известную как « Скаты и лестницы» ):

Правила игры следующие:

  • Доска имеет 25 квадратов, и цель состоит в том, чтобы приземлиться на или за квадрат 25.
  • Начальная клетка игрока - «квадрат ноль», который находится за левым нижним углом доски.
  • Каждый ход вы бросаете шестигранный кубик и перемещаетесь на это количество квадратов, следуя горизонтальному пути, указанному пунктирной стрелкой выше.
  • Если ваш ход заканчивается у основания лестницы, вы двигаетесь вверх по этой лестнице.
  • Если ваш ход заканчивается во главе змеи, вы двигаетесь вниз по этой змеи.

Игровое поле представлено массивом Intзначений. Его размер основан на константе с именем finalSquare, которая используется для инициализации массива, а также для проверки условия выигрыша позже в этом примере. Поскольку игроки начинают с доски, на «квадратном нуле» доска инициализируется 26 нулевыми Intзначениями, а не 25.

let finalSquare = 25

var board = [Int](repeating: 0, count: finalSquare + 1)

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

board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02

board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08

Квадрат 3 содержит нижнюю часть лестницы, которая поднимает вас до квадрата 11. Чтобы представить это, board[03]равен +08, что эквивалентно целочисленному значению 8(разница между 3и 11). Для выравнивания значений и операторов унарный оператор плюс ( +i) явно используется с унарным оператором минус ( -i) и числами ниже, чем 10дополняются нулями. (Никакая стилистическая техника не является строго необходимой, но они приводят к более аккуратному коду.)

var square = 0

var diceRoll = 0

while square < finalSquare {

// roll the dice

diceRoll += 1

if diceRoll == 7 { diceRoll = 1 }

// move by the rolled amount

square += diceRoll

if square < board.count {

// if we're still on the board, move up or down for a snake or a ladder

square += board[square]

}

}

print("Game over!")

В приведенном выше примере используется очень простой подход к игре в кости. Вместо генерации случайного числа оно начинается со diceRollзначения 0. Каждый раз в whileцикле diceRollувеличивается на единицу, а затем проверяется, не стало ли оно слишком большим. Всякий раз, когда это возвращаемое значение равно 7, бросок костей становится слишком большим и сбрасывается до значения 1. Результат представляет собой последовательность diceRollзначений, которая всегда 12345612и так далее.

После броска костей игрок продвигается вперед на diceRollквадраты. Вполне возможно, что бросок костей переместил игрока за пределы поля 25, и в этом случае игра окончена. Чтобы справиться с этим сценарием, код проверяет, что squareменьше, чем свойство boardмассива count. Если squareон действителен, значение, сохраненное в board[square], добавляется к текущему squareзначению, чтобы переместить игрока вверх или вниз по любым лестницам или змеям.

ЗАМЕТКА

Если эта проверка не выполняется, board[square]можно попытаться получить доступ к значению за пределами boardмассива, что приведет к ошибке времени выполнения.

Затем выполнение текущего whileцикла заканчивается, и проверяется состояние цикла, чтобы увидеть, должен ли цикл быть выполнен снова. Если игрок перешел на квадратное число или за его пределы 25, условие цикла оценивается falseи игра заканчивается.

whileПетля подходит в данном случае, так как длина игры не ясно , в начале whileцикла. Вместо этого цикл выполняется до тех пор, пока не будет выполнено определенное условие.

Repeat-While

Другой вариант whileцикла, известный как repeatwhileцикл, сначала выполняет один проход через блок цикла, прежде чем рассмотреть условие цикла. Затем он продолжает повторять цикл, пока не будет выполнено условие false.

ЗАМЕТКА

repeatwhileпетля в Swift аналогична dowhileпетли на других языках.

Вот общая форма цикла repeatwhile:

repeat {

statements

} while condition

Вот Змеи и лестницы пример еще раз, записывается в виде repeatwhileпетли , а не whileпетли. Значения finalSquareboardsquareи diceRollинициализируются точно таким же образом , как и с whileпетлей.

let finalSquare = 25

var board = [Int](repeating: 0, count: finalSquare + 1)

board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02

board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08

var square = 0

var diceRoll = 0

В этой версии игры первым действием в цикле является проверка на наличие лестницы или змеи. Никакая лестница на игровом поле не приводит игрока прямо к клетке 25, и поэтому невозможно выиграть игру, поднявшись по лестнице. Следовательно, в качестве первого действия в цикле безопасно проверить наличие змеи или лестницы.

В начале игры игрок находится на «квадрате ноль». board[0]всегда равно 0и не имеет никакого эффекта.

repeat {

// move up or down for a snake or ladder

square += board[square]

// roll the dice

diceRoll += 1

if diceRoll == 7 { diceRoll = 1 }

// move by the rolled amount

square += diceRoll

} while square < finalSquare

print("Game over!")

После того, как код проверяет наличие змей и лестниц, игра в кости бросается, и игрок продвигается вперед на diceRollквадраты. Выполнение текущего цикла затем заканчивается.

Условие цикла ( ) такое же, как и раньше, но на этот раз оно не оценивается до концапервого цикла цикла. Структура - петли лучше подходит к этой игре , чем цикл в предыдущем примере. В - петли выше, всегда выполняется сразу же после того, как Петля в состояние подтверждает , что все еще находится на борту. Такое поведение устраняет необходимость в проверке границ массива, которую можно увидеть в циклической версии игры, описанной ранее.while square < finalSquarerepeatwhilewhilerepeatwhilesquare += board[square]whilesquarewhile

Условные заявления

Часто полезно выполнять разные фрагменты кода в зависимости от определенных условий. Возможно, вы захотите запустить дополнительный фрагмент кода при возникновении ошибки или отобразить сообщение, когда значение становится слишком высоким или слишком низким. Для этого вы делаете части своего кода условными .

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

Если

В простейшей форме ifутверждение имеет единственное ifусловие. Он выполняет набор операторов, только если это условие true.

var temperatureInFahrenheit = 30

if temperatureInFahrenheit <= 32 {

print("It's very cold. Consider wearing a scarf.")

}

// Prints "It's very cold. Consider wearing a scarf."

В приведенном выше примере проверяется, является ли температура меньше или равна 32 градусам Фаренгейта (точка замерзания воды). Если это так, сообщение печатается. В противном случае сообщение не печатается, и выполнение кода продолжается после ifзакрывающей скобки оператора.

ifОператор может обеспечить альтернативный набор операторов, известные как п - либо , для ситуаций , когда ifусловие false. Эти заявления обозначены elseключевым словом.

temperatureInFahrenheit = 40

if temperatureInFahrenheit <= 32 {

print("It's very cold. Consider wearing a scarf.")

} else {

print("It's not that cold. Wear a t-shirt.")

}

// Prints "It's not that cold. Wear a t-shirt."

Одна из этих двух веток всегда выполняется. Поскольку температура повысилась до 40градусов по Фаренгейту, уже недостаточно холодно, чтобы рекомендовать носить шарф, и elseвместо этого срабатывает ветвь.

Вы можете связать несколько ifоператоров вместе, чтобы рассмотреть дополнительные предложения.

temperatureInFahrenheit = 90

if temperatureInFahrenheit <= 32 {

print("It's very cold. Consider wearing a scarf.")

} else if temperatureInFahrenheit >= 86 {

print("It's really warm. Don't forget to wear sunscreen.")

} else {

print("It's not that cold. Wear a t-shirt.")

}

// Prints "It's really warm. Don't forget to wear sunscreen."

Здесь ifбыло добавлено дополнительное заявление, чтобы ответить на особенно теплые температуры. Последний elseпункт остается, и он печатает ответ для любых температур, которые не являются ни слишком теплыми, ни слишком холодными.

Однако последний elseпункт является необязательным и может быть исключен, если набор условий не требуется выполнять.

temperatureInFahrenheit = 72

if temperatureInFahrenheit <= 32 {

print("It's very cold. Consider wearing a scarf.")

} else if temperatureInFahrenheit >= 86 {

print("It's really warm. Don't forget to wear sunscreen.")

}

Поскольку температура не слишком низкая и не слишком высокая, чтобы вызвать условия ifили , сообщение не печатается.else if

переключатель

switchЗаявление рассматривает значение и сравнивает его с нескольких возможных моделей соответствия. Затем он выполняет соответствующий блок кода на основе первого шаблона, который успешно соответствует. switchЗаявление предоставляет альтернативу ifзаявления для реагирования на несколько возможных состояний.

В своей простейшей форме switchоператор сравнивает значение с одним или несколькими значениями одного и того же типа.

switch some value to consider {

case value 1:

respond to value 1

case value 2,

value 3:

respond to value 2 or 3

default:

otherwise, do something else

}

Каждое switchутверждение состоит из нескольких возможных случаев , каждый из которых начинается с caseключевого слова. В дополнение к сравнению с конкретными значениями, Swift предоставляет несколько способов для каждого случая, чтобы указать более сложные шаблоны сопоставления. Эти параметры описаны ниже в этой главе.

Как и тело ifоператора, каждый caseявляется отдельной ветвью выполнения кода. switchОператор определяет , какая ветвь должна быть выбрана. Эта процедура называется включением значения, которое рассматривается.

Каждое switchутверждение должно быть исчерпывающим . То есть каждое возможное значение рассматриваемого типа должно соответствовать одному из switchслучаев. Если не уместно указывать регистр для каждого возможного значения, вы можете определить регистр по умолчанию, чтобы охватить любые значения, которые не адресованы явно. Этот регистр по умолчанию обозначается defaultключевым словом и всегда должен быть последним.

В этом примере используется switchинструкция для рассмотрения одного символа нижнего регистра someCharacter:

let someCharacter: Character = "z"

switch someCharacter {

case "a":

print("The first letter of the alphabet")

case "z":

print("The last letter of the alphabet")

default:

print("Some other character")

}

// Prints "The last letter of the alphabet"

В switchпервом сазе совпадает с первой буквой английского алфавита, aи его второй случай соответствует последней букве z. Поскольку switchаргумент должен иметь регистр для каждого возможного символа, а не только для каждого буквенного символа, этот switchоператор использует defaultрегистр для сопоставления всех символов, кроме aи z. Это положение гарантирует, что switchзаявление является исчерпывающим.

Нет неявного падения

В отличие от switchоператоров в C и Objective-C, switchоператоры в Swift по умолчанию не попадают в нижнюю часть каждого случая и попадают в следующий. Вместо этого весь switchоператор завершает свое выполнение, как только завершается первый соответствующий switchслучай, не требуя явного breakоператора. Это делает switchинструкцию более безопасной и простой в использовании, чем в Си, и позволяет избежать выполнения более одного switchслучая по ошибке.

ЗАМЕТКА

Хотя breakэто не требуется в Swift, вы можете использовать breakоператор для сопоставления и игнорирования конкретного случая или выхода из сопоставленного случая до того, как этот случай завершит свое выполнение. Для получения дополнительной информации см. Разрыв в операторе переключения .

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

let anotherCharacter: Character = "a"

switch anotherCharacter {

case "a": // Invalid, the case has an empty body

case "A":

print("The letter A")

default:

print("Not the letter A")

}

// This will report a compile-time error.

В отличие от switchоператора в C, это switchутверждение не соответствует как "a"и "A". Скорее, он сообщает об ошибке времени компиляции, которая не содержит никаких исполняемых операторов. Этот подход позволяет избежать случайного падения из одного случая в другой и обеспечивает более безопасный код, более понятный по своему замыслу.case "a":

Чтобы создать switchс одним регистром, который соответствует обоим "a"и "A", объедините два значения в составной регистр, разделяя значения запятыми.

let anotherCharacter: Character = "a"

switch anotherCharacter {

case "a", "A":

print("The letter A")

default:

print("Not the letter A")

}

// Prints "The letter A"

Для удобства чтения составной регистр также может быть записан в несколько строк. Для получения дополнительной информации о составных случаях см. Составные случаи .

ЗАМЕТКА

Чтобы явно провалиться в конце конкретного switchслучая, используйте fallthroughключевое слово, как описано в Fallthrough .

Интервал соответствия

Значения в switchслучаях можно проверить на предмет их включения в интервал. В этом примере используются числовые интервалы для обеспечения счета на естественном языке для чисел любого размера:

let approximateCount = 62

let countedThings = "moons orbiting Saturn"

let naturalCount: String

switch approximateCount {

case 0:

naturalCount = "no"

case 1..<5:

naturalCount = "a few"

case 5..<12:

naturalCount = "several"

case 12..<100:

naturalCount = "dozens of"

case 100..<1000:

naturalCount = "hundreds of"

default:

naturalCount = "many"

}

print("There are \(naturalCount) \(countedThings).")

// Prints "There are dozens of moons orbiting Saturn."

В приведенном выше примере, approximateCountоценивается в switchзаявлении. Каждый caseсравнивает это значение с числом или интервалом. Поскольку значение approximateCountпадает между 12 и 100, naturalCountприсваивается значение , и исполнение переносится из оператора."dozens of"switch

Кортеж

Вы можете использовать кортежи для проверки нескольких значений в одном switchвыражении. Каждый элемент кортежа может быть проверен по разному значению или интервалу значений. В качестве альтернативы используйте символ подчеркивания ( _), также известный как шаблон подстановки, для сопоставления с любым возможным значением.

В приведенном ниже примере берется точка (x, y), выраженная в виде простого кортежа типа , и она классифицируется на графике, который следует за примером.(Int, Int)

let somePoint = (1, 1)

switch somePoint {

case (0, 0):

print("\(somePoint) is at the origin")

case (_, 0):

print("\(somePoint) is on the x-axis")

case (0, _):

print("\(somePoint) is on the y-axis")

case (-2...2, -2...2):

print("\(somePoint) is inside the box")

default:

print("\(somePoint) is outside of the box")

}

// Prints "(1, 1) is inside the box"

switchУтверждение определяет , находится ли точка находится в начале координат (0, 0), на красной оси х, на оранжевой оси у, внутри синей коробке 4-на-4 с центром в начале координат, или вне коробки.

В отличие от C, Swift позволяет нескольким switchслучаям рассматривать одно и то же значение или значения. Фактически, точка (0, 0) может соответствовать всем четыремслучаям в этом примере. Однако, если возможно несколько совпадений, всегда используется первый случай совпадения. Точка (0, 0) будет соответствовать первой, и поэтому все другие соответствующие случаи будут игнорироваться.case (0, 0)

Привязки значений

switchСлучай может назвать значение или значение , что соответствует временным константам или переменным, для использования в теле корпуса. Такое поведение известно как привязка значений , поскольку значения привязаны к временным константам или переменным в теле кейса.

В приведенном ниже примере берется точка (x, y), выраженная в виде кортежа типа , и она классифицируется на следующем графике:(Int, Int)

let anotherPoint = (2, 0)

switch anotherPoint {

case (let x, 0):

print("on the x-axis with an x value of \(x)")

case (0, let y):

print("on the y-axis with a y value of \(y)")

case let (x, y):

print("somewhere else at (\(x), \(y))")

}

// Prints "on the x-axis with an x value of 2"

switchУтверждение определяет , находится ли точка на красной оси х, на оранжевой оси у, или в другом месте (на оси ни).

В трех switchслучаях объявляются константы-заполнители xи y, которые временно принимают одно или оба значения кортежа из anotherPoint. В первом случае, соответствует любой точке со значением и присваивает значение точки временной константе . Аналогично, второй случай, сопоставляет любую точку со значением и присваивает значение точки временной константе .case (let x, 0)y0xxcase (0, let y)x0yy

После того, как временные константы объявлены, они могут использоваться в блоке кода дела. Здесь они используются для печати категоризации точки.

Это switchутверждение не имеет defaultдела. В последнем случае объявляется кортеж из двух констант-заполнителей, которые могут соответствовать любому значению. Поскольку это всегда кортеж из двух значений, этот регистр соответствует всем возможным оставшимся значениям, и регистр не требуется для того, чтобы сделать инструкцию исчерпывающей.case let (x, y)anotherPointdefaultswitch

куда

switchСлучай можно использовать whereпункт для проверки дополнительных условий.

Пример ниже классифицирует точку (x, y) на следующем графике:

let yetAnotherPoint = (1, -1)

switch yetAnotherPoint {

case let (x, y) where x == y:

print("(\(x), \(y)) is on the line x == y")

case let (x, y) where x == -y:

print("(\(x), \(y)) is on the line x == -y")

case let (x, y):

print("(\(x), \(y)) is just some arbitrary point")

}

// Prints "(1, -1) is on the line x == -y"

 

switchОператор определяет , находится ли точка находится на зеленой диагональной линии , где на фиолетовом диагональной линии , где , или ни.x == yx == -y

В трех switchслучаях объявляются константы-заполнители xи y, которые временно принимают два значения кортежа из yetAnotherPoint. Эти константы используются как часть whereпредложения для создания динамического фильтра. switchСлучай соответствует текущему значению , pointтолько если whereусловие вычисляется Морозом , чтобы trueдля этого значения.

Как и в предыдущем примере, окончательный регистр соответствует всем возможным оставшимся значениям, поэтому defaultрегистр не требуется, чтобы сделать switchинструкцию исчерпывающей.

Сложные случаи

Несколько вариантов переключения, которые имеют одно и то же тело, можно объединить, написав несколько шаблонов после case, с запятой между каждым из шаблонов. Если какой-либо шаблон соответствует, то случай считается соответствующим. Шаблоны могут быть записаны в несколько строк, если список длинный. Например:

let someCharacter: Character = "e"

switch someCharacter {

case "a", "e", "i", "o", "u":

print("\(someCharacter) is a vowel")

case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",

"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":

print("\(someCharacter) is a consonant")

default:

print("\(someCharacter) is not a vowel or a consonant")

}

// Prints "e is a vowel"

В switchпервом сазе совпадает все пять строчных гласных в английском языке. Аналогично, его второй регистр соответствует всем строчным английским согласным. Наконец, defaultрегистр соответствует любому другому символу.

Сложные случаи также могут включать привязки значений. Все шаблоны составного случая должны включать в себя один и тот же набор привязок значений, и каждая привязка должна получать значение одного и того же типа из всех шаблонов в составном случае. Это гарантирует, что независимо от того, какая часть составного случая совпадает, код в теле случая всегда может получить доступ к значению для привязок, и что значение всегда имеет один и тот же тип.

let stillAnotherPoint = (9, 0)

switch stillAnotherPoint {

case (let distance, 0), (0, let distance):

print("On an axis, \(distance) from the origin")

default:

print("Not on an axis")

}

// Prints "On an axis, 9 from the origin"

caseВыше , имеет две модели: совпадения точек на оси х и соответствует точек на оси у. Оба шаблона включают в себя привязку и являются целым числом в обоих шаблонах, что означает, что код в теле может всегда иметь доступ к значению для .(let distance, 0)(0, let distance)distancedistancecasedistance

Заявления о передаче управления

Операторы передачи управления изменяют порядок, в котором выполняется ваш код, путем передачи управления от одного фрагмента кода к другому. Swift имеет пять операторов передачи управления:

  • continue
  • break
  • fallthrough
  • return
  • throw

Операторы continuebreakи fallthroughописаны ниже. Оператор returnописан в разделе « Функции» , а throwоператор - в разделе « Распространение ошибок с использованием бросающих функций» .

Продолжить

continueОператор говорит цикл , чтобы остановить то , что он делает , и начать снова в начале следующей итерации цикла. Он говорит: «Я закончил с текущей итерацией цикла», не выходя из цикла вообще.

В следующем примере удаляются все гласные и пробелы из строчной строки для создания загадочной фразы-головоломки:

let puzzleInput = "great minds think alike"

var puzzleOutput = ""

let charactersToRemove: [Character] = ["a", "e", "i", "o", "u", " "]

for character in puzzleInput {

if charactersToRemove.contains(character) {

continue

}

puzzleOutput.append(character)

}

print(puzzleOutput)

// Prints "grtmndsthnklk"

Приведенный выше код вызывает continueключевое слово всякий раз, когда оно соответствует гласному или пробелу, в результате чего текущая итерация цикла немедленно завершается и переходит прямо к началу следующей итерации.

Перерыв

breakОператор заканчивается выполнение всего отчета о движении управления немедленно. breakЗаявление может быть использовано внутри switchоператора или цикла , когда вы хотите прервать выполнение switchоператора или цикла раньше , чем было бы в противном случае.

Перерыв в цикле

При использовании внутри инструкции breakцикла немедленно завершает выполнение цикла и передает управление коду после закрывающей скобки цикла ( }). Никакой дополнительный код из текущей итерации цикла не выполняется, и дальнейшие итерации цикла не запускаются.

Перерыв в операторе переключения

При использовании внутри switchоператора breakзаставляет switchоператор немедленно завершать его выполнение и передавать управление в код после switchзакрывающей скобки оператора ( }).

Это поведение можно использовать для сопоставления и игнорирования одного или нескольких случаев в switchвыражении. Поскольку switchутверждение Свифта является исчерпывающим и не допускает пустых падежей, иногда необходимо преднамеренно сопоставлять и игнорировать регистр, чтобы сделать ваши намерения явными. Вы делаете это, записывая breakзаявление как весь текст дела, которое хотите игнорировать. Когда этот случай совпадает с switchоператором, breakоператор внутри случая switchнемедленно завершает выполнение оператора.

ЗАМЕТКА

switchСлучай , который содержит только комментарий сообщается как ошибка времени компиляции. Комментарии не являются заявлениями и не приводят switchк игнорированию дела. Всегда используйте breakоператор, чтобы игнорировать switchрегистр.

В следующем примере включается Characterзначение и определяется, представляет ли оно символ числа на одном из четырех языков. Для краткости несколько значений рассматриваются в одном switchслучае.

let numberSymbol: Character = "三" // Chinese symbol for the number 3

var possibleIntegerValue: Int?

switch numberSymbol {

case "1", "١", "一", "๑":

possibleIntegerValue = 1

case "2", "٢", "二", "๒":

possibleIntegerValue = 2

case "3", "٣", "三", "๓":

possibleIntegerValue = 3

case "4", "٤", "四", "๔":

possibleIntegerValue = 4

default:

break

}

if let integerValue = possibleIntegerValue {

print("The integer value of \(numberSymbol) is \(integerValue).")

} else {

print("An integer value could not be found for \(numberSymbol).")

}

// Prints "The integer value of 三 is 3."

Этот пример проверяет , numberSymbolчтобы определить , является ли это латинский, арабский, китайский, тайский или символ для чисел 1до 4. Если совпадение найдено, один из switchслучаев оператора устанавливает необязательную Int?переменную, вызываемую possibleIntegerValueдля соответствующего целочисленного значения.

После того, как switchинструкция завершает свое выполнение, пример использует необязательную привязку, чтобы определить, было ли найдено значение. possibleIntegerValueПеременная имеет неявный начальное значение nilв силу того , необязательный тип, и поэтому дополнительное связывание будет успешным , только если possibleIntegerValueбыл установлен на фактическое значение одного из switchЗаявление о первых четырех случаях.

Поскольку перечислять все возможные Characterзначения в приведенном выше примере нецелесообразно , defaultрегистр обрабатывает любые символы, которые не совпадают. В этом defaultслучае не требуется выполнять какие-либо действия, поэтому он записывается с одним breakоператором в качестве тела. Как только defaultсопоставляется регистр, breakоператор завершает выполнение switchоператора, и выполнение кода продолжается с оператора.if let

Провалиться

В Swift switchоператоры не попадают в нижнюю часть каждого дела и переходят к следующему. Таким образом, весь switchоператор завершает свое выполнение, как только первый соответствующий случай завершен. В отличие от этого, C требует, чтобы вы вставляли явное breakутверждение в конце каждого switchслучая, чтобы предотвратить падение. Предотвращение падения по умолчанию означает, что switchоператоры Swift намного более кратки и предсказуемы, чем их аналоги в C, и, таким образом, они избегают выполнения нескольких switchслучаев по ошибке.

Если вам нужно поведение при падении в стиле C, вы можете выбрать это поведение в каждом конкретном случае с помощью fallthroughключевого слова. В приведенном ниже примере используется fallthroughдля создания текстового описания числа.

let integerToDescribe = 5

var description = "The number \(integerToDescribe) is"

switch integerToDescribe {

case 2, 3, 5, 7, 11, 13, 17, 19:

description += " a prime number, and also"

fallthrough

default:

description += " an integer."

}

print(description)

// Prints "The number 5 is a prime number, and also an integer."

В этом примере объявляется новая Stringпеременная с именем descriptionи присваивается ей начальное значение. Затем функция рассматривает значение integerToDescribeиспользования switchоператора. Если значение integerToDescribeявляется одним из простых чисел в списке, функция добавляет текст в конец description, чтобы отметить, что число является простым. Затем он также использует fallthroughключевое слово, чтобы «попасть в» defaultдело. defaultСлучай добавляет дополнительный текст в конце описания, и switchутверждение завершено.

Если значение не integerToDescribeнаходится в списке известных простых чисел, оно вообще не соответствует первому switchслучаю. Потому что нет никаких других конкретных случаев, integerToDescribeсоответствует defaultделу.

После того, как switchоператор завершил выполнение, описание номера печатается с использованием print(_:separator:terminator:)функции. В этом примере число 5правильно идентифицируется как простое число.

ЗАМЕТКА

fallthroughКлючевое слово не проверяет условия случае для switchслучая , что вызывает исполнение впасть в. fallthroughКлючевое слово просто вызывает выполнение кода , чтобы перейти непосредственно к высказываниям внутри следующий случай (или defaultслучай) блока, как и в стандартном C в switchповедении операторов.

Помеченные заявления

В Swift вы можете вкладывать циклы и условные операторы в другие циклы и условные операторы для создания сложных структур потока управления. Однако циклы и условные операторы могут использовать breakоператор для преждевременного завершения выполнения. Следовательно, иногда полезно явно указать, какой цикл или условный оператор вы хотите breakзавершить. Точно так же, если у вас есть несколько вложенных циклов, может быть полезно четко указать, на какой цикл continueдолжен влиять оператор.

Для достижения этих целей вы можете пометить оператор цикла или условный оператор меткой оператора . С условным оператором вы можете использовать метку оператора с breakоператором, чтобы завершить выполнение помеченного оператора. С оператором цикла вы можете использовать метку оператора с оператором breakили, continueчтобы завершить или продолжить выполнение помеченного оператора.

Помеченный оператор указывается путем размещения метки в той же строке, что и ключевое слово вводителя оператора, за которым следует двоеточие. Вот пример этого синтаксиса для whileцикла, хотя принцип одинаков для всех циклов и switchоператоров:

label name: while condition {

statements

}

В следующем примере используется breakи continueоператоры с меченой whileпетлей для адаптированной версии Змеи и лестницы игры , которые вы видели ранее в этой главе. На этот раз в игре есть дополнительное правило:

  • Чтобы выиграть, вы должны приземлиться ровно на 25 клетке.

Если конкретный бросок костей выведет вас за пределы квадрата 25, вы должны бросить снова, пока вы не бросите точное число, необходимое для посадки на клетку 25.

Игровая доска такая же, как и раньше.

 

Значения finalSquareboardsquareи diceRollинициализируются таким же образом , как и раньше:

let finalSquare = 25

var board = [Int](repeating: 0, count: finalSquare + 1)

board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02

board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08

var square = 0

var diceRoll = 0

Эта версия игры использует whileцикл и switchоператор для реализации логики игры. whileЦикл имеет метку заявления под названием , gameLoopчтобы указать , что основной цикл игры для игры Змеи и лестницы.

В whileусловии цикла является , чтобы отразить , что вы должны точно приземлиться на площади 25.while square != finalSquare

gameLoop: while square != finalSquare {

diceRoll += 1

if diceRoll == 7 { diceRoll = 1 }

switch square + diceRoll {

case finalSquare:

// diceRoll will move us to the final square, so the game is over

break gameLoop

case let newSquare where newSquare > finalSquare:

// diceRoll will move us beyond the final square, so roll again

continue gameLoop

default:

// this is a valid move, so find out its effect

square += diceRoll

square += board[square]

}

}

print("Game over!")

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

  • Если бросок костей переместит игрока на последний квадрат, игра окончена. В передает заявление управления в первой строке кода за пределами цикла, который заканчивает игру.break gameLoopwhile
  • Если бросок костей переместит игрока за пределы последнего квадрата, ход будет недействительным, и игрок должен снова бросить. Оператор завершает текущую итерацию цикла и начинает следующую итерацию цикла.continue gameLoopwhile
  • Во всех других случаях бросок костей является допустимым ходом. Игрок продвигается вперед на diceRollквадраты, а логика игры проверяет наличие змей и лестниц. Затем цикл заканчивается, и управление возвращается к whileусловию, чтобы решить, требуется ли еще один оборот.

ЗАМЕТКА

Если в breakвышеприведенном утверждении не используется gameLoopметка, то оно вырвется из switchутверждения, а не из whileутверждения. Использование gameLoopметки дает понять, какой оператор управления должен быть завершен.

Нет необходимости использовать gameLoopметку при вызове для перехода к следующей итерации цикла. В игре только один цикл, и, следовательно, нет двусмысленности относительно того, на какой цикл повлияет это утверждение. Однако использование ярлыка с заявлением не повредит . Это согласуется с использованием ярлыка вместе с утверждением и помогает сделать логику игры более понятной для чтения и понимания.continue gameLoopcontinuegameLoopcontinuebreak

Ранний выход

guardЗаявление, как ifзаявление, выполняет операторы в зависимости от логического значения выражения. Вы используете guardоператор, чтобы требовать, чтобы условие было истинным, чтобы код после guardоператора был выполнен. В отличие от ifоператора, guardоператор всегда имеет elseпредложение - код внутри elseпредложения выполняется, если условие не выполняется.

func greet(person: [String: String]) {

guard let name = person["name"] else {

return

}



print("Hello \(name)!")



guard let location = person["location"] else {

print("I hope the weather is nice near you.")

return

}



print("I hope the weather is nice in \(location).")

}



greet(person: ["name": "John"])

// Prints "Hello John!"

// Prints "I hope the weather is nice near you."

greet(person: ["name": "Jane", "location": "Cupertino"])

// Prints "Hello Jane!"

// Prints "I hope the weather is nice in Cupertino."

Если guardусловие оператора выполнено, выполнение кода продолжается после guardзакрывающей скобки оператора. Любые переменные или константы, которым были присвоены значения с использованием необязательной привязки как части условия, доступны для остальной части блока кода, в guardкотором появляется инструкция.

Если это условие не выполняется, выполняется код внутри elseветви. Эта ветвь должна передать управление, чтобы выйти из блока кода, в котором guardпоявляется инструкция. Он может сделать это с заявлением передачи управления , например returnbreakcontinue, или throw, или это может вызвать функцию или метод , который не возвращается, такие как fatalError(_:file:line:).

Использование guardоператора для требований улучшает читабельность вашего кода по сравнению с выполнением той же проверки с ifоператором. Он позволяет вам писать код, который обычно выполняется, не заключая его в elseблок, и позволяет хранить код, который обрабатывает нарушенное требование, рядом с требованием.

Проверка доступности API

Swift имеет встроенную поддержку для проверки доступности API, которая гарантирует, что вы не будете случайно использовать API, которые недоступны для данной цели развертывания.

Компилятор использует информацию о доступности в SDK, чтобы убедиться, что все API-интерфейсы, используемые в вашем коде, доступны для цели развертывания, указанной вашим проектом. Swift сообщает об ошибке во время компиляции, если вы пытаетесь использовать API, который недоступен.

Вы используете условие доступности в операторе ifили guardдля условного выполнения блока кода в зависимости от того, доступны ли API, которые вы хотите использовать, во время выполнения. Компилятор использует информацию из условия доступности, когда он проверяет, что API в этом блоке кода доступны.

if #available(iOS 10, macOS 10.12, *) {

// Use iOS 10 APIs on iOS, and use macOS 10.12 APIs on macOS

} else {

// Fall back to earlier iOS and macOS APIs

}

Приведенное выше условие доступности указывает, что в iOS тело ifинструкции выполняется только в iOS 10 и более поздних версиях; в macOS, только в macOS 10.12 и выше. Последний аргумент, *является обязательным и указывает, что на любой другой платформе тело ifисполняется с минимальной целью развертывания, указанной вашей целью.

В общем виде условие доступности принимает список названий и версий платформ. Вы можете использовать название платформы , такие как iOSmacOSwatchOS, и tvOS-дль полного списка см Декларации атрибутов . В дополнение к указанию основных номеров версий, таких как iOS 8 или macOS 10.10, вы можете указать дополнительные номера версий, таких как iOS 11.2.6 и macOS 10.13.3.

if #available(platform name version, ..., *) {

statements to execute if the APIs are available

} else {

fallback statements to execute if the APIs are unavailable

}