obo.dev

Управление потоком (Flow Control)

05 Dec 2022

Управление потоком (Flow Control)

Код в программе выполняется последовательно, сверху вниз, от строчки к строчке. Создается поток кода. И компилятор считывает код так же - от строки к строке.

Это похоже на речной поток. Река не может просто так прерваться и возникнуть в другом месте. Чтоб это стало возможно - необходимо создать, например, какой-то тунель или переход.

В программировании похожая ситуация. Раньше программы были более простыми и использовался для таких переходов оператор “goto”, который позволял перейти к необходимой строке кода. Таким образом поток программы не прерывался, а переходил из одной строки в другую.

В современном программировании программы стали сложнее, объёмнее, составляют многие сотни строчек кода. Логика современных программ более сложна и алгоритмы решения задач требуют либо разделения потока в зависимости от условий, либо повторения части кода при определённых условиях. В первом случае, создаются ветвления (Branch), во втором - циклы (петли, Loop).

Немного про алгоритмы

Схема алгоритма — графическое представление алгоритма, дополняемое элементами словесной записи. Каждый пункт алго­ритма отображается на схеме некоторой геометрической фигу­рой или блоком. При этом правило выполнения схем алгорит­мов регламентирует ГОСТ 19.002—80 «Единая система про­граммной документации».

Блоки на схемах соединяются линиями потоков информа­ции. Основное направление потока информации идет сверху вниз и слева направо (стрелки могут не указываться), снизу вверх и справа налево — стрелка обязательна. Количество входя­щих линий для блока не ограничено. Выходящая линия — одна, за исключением логического блока.

К основным структурам относятся следующие — линейные, разветвляющиеся, циклические.

Примеры структур алгоритмов

На этом изображении - Базовые структуры алгоритмов и программ:

  • а - линейный алгоритм;
  • б - алгоритм с ветвлением;
  • в - алгоритм с циклом While (Пока);
  • г — алгоритм с циклом For (До);
  • д — пример алгоритма вычисления факториала.

Линейными называются алгоритмы, в которых действия осу­ществляются последовательно друг за другом. Стандартная блок-схема линейного алгоритма приводится на рисунке (изображение а).

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

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

Циклическим называется алгоритм, в котором некоторая последовательность операций (тело цикла) выполняется многократно. Однако «многократно» не означает «до бесконечности». Организа­ция циклов, никогда не приводящая к остановке в выполнении ал­горитма, является нарушением требования его результативности — получения результата за конечное число шагов (изображение в).

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

Если тело цикла расположено после проверки условий (цикл с предусловием), то может случиться, что при определенных условиях блок «тело цикла» не выполнится ни разу. Такой вариант организации цикла, управляемый предусловием, называется цикл “Пока” (While) — здесь условие — это условие продолжения цикла.

Возможен другой случай, когда тело цикла выполняется, по крайней мере, один раз и будет повторяться до тех пор, пока не станет истинным условие. Такая организация цикла, когда его тело расположено перед проверкой условия, носит название цикла с постусловием, или цикла “До” (For).

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

Пример алгоритма вычисления факториала, изображенный на рисунке выше (фиг. д) (с циклом “Пока”). Переменная N получает значение числа, факториал которого вычисляется. Переменной N, которая в результате выполнения алгоритма должна получить значение факториала, присваивается первоначальное значение “1”. Переменной К также присваивается значение “1”. Цикл будет выполняться, пока справедливо условие “N > К”. Тело цикла состоит из двух операций “N = N x K” и “К = К + 1”.

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

Алгоритм ветвления указан на рисунке в общем виде. Возможно несколько видов данного алгоритма.

  1. Алгоритм ветвления 1 - “Если …, то …”.

Примеры алгоритма ветвления "Если ..., то ..."

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

  1. Алгоритм ветвления 2 - “Если …, то …; иначе …”.

Примеры алгоритма ветвления "Если ..., то ...; иначе ..."

Работа алгоритма. Если условие выполнится, то будет выполнено определенное действие. Иначе, если условие не выполниться, то будет выполнено другое действие. После ветвления выполнение алгоритма пойдёт дальше.

  1. Алгоритм ветвления 3 - “Если …, то …; еще если …, то …; …; иначе …”.

Примеры алгоритма ветвления "Если ..., то ...; еще если ..., то ...; ...; иначе ..."

Работа алгоритма. Если условие выполнится, то будет выполнено определенное действие. Иначе, если условие не выполнится, то будет проверено следующее условие и если оно будет выполнено - будет выполнено определенное действие. Дальше, таким же образом проверяются остальные условия. Иначе, если ни одно условие не выполниться, то будет выполнено еще одно действие. После ветвления выполнение алгоритма пойдёт дальше.

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

Процесс построения алгоритма методом последовательной детализации состоит в следующем. Сначала алгоритм формулируется в «крупных» блоках (командах), которые могут быть непонятны исполнителю (не входят в его систему команд), и записываются как вызовы вспомогательных алгоритмов. Затем происходит детализация, и все вспомогательные алгоритмы подробно расписываются с использованием команд, понятных исполнителю.

Условные выражения

Условные выражения используются для принятие решений в вашем коде:

если условие верно, сделай это

К примеру:

  • Если пользователь успешно залогинен, мы загружаем его твиты;
  • Если твит содержит фотографию, мы используем особый интерфейс для ее отображения;
  • Если твит содержит определенные слова, мы генерируем хэштеги;
  • Принятие подобных решений называется управлением потока кода, потому что мы разделяем поток кода на отдельные ветви.

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

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

Циклы используют условные выражения (в явном и неявном виде) для проверки условия выполнения самого цикла. Циклы (петли) заставляют выполнять часть кода повторно в зависимости от условий (условных выражений). И имеют также блок с кодом, который повторяется необходимое количество раз (тело цикла). Поэтому такие операторы чаще называют операторами цикла.

Булева логика

Условные выражения используют булеву логику для возврата либо true, либо false. В Swift данный тип обозначается как Bool.

Пример:

let isLoggedIn: Bool = false
 
if isLoggedIn {
    print("Секретное сообщение")
}

В этом примере, условное выражение isLoggedIn указано после оператора if. Его значение оценивается как ложное (false), так как значение константы isLoggedIn напрямую указано как false. Если значение поменять на правдивое (true), тогда условие выполниться и код внутри блока if будет выполнен.

Также можно получить значение условного выражения с помощью операторов сравнения или логических операторов.

Условные выражения и операторы сравнения

Swift имеет следующие операторы сравнения:

  • Равно: a == b;
  • Не равно: a != b;
  • Больше, чем: a > b;
  • Меньше, чем: a < b;
  • Больше или равно: a >= b;
  • Меньше или равно: a <= b.

Результатом выполнения даных операторов будет логическое значение (true, false) по типу Bool.

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

let room: Int = 123
let input: String = "123"
 
if room == input {
    print("Комната недоступна")
}

Можно даже сравнивать строки друг с другом. Таким образом, мы можем проверить, меньше ли «abcd», чем «efgh»:

"abcd" < "efgh"
// true

Выражение:

if isLoggedIn {
    print("Секретное сообщение")
}

проверяет, равно ли значение isLoggedIn true. Этот код также можно написать следующим образом:

if isLoggedIn == true {
    // 
}

Условные выражения и логические операторы

Также доступны логические операторы.

Есть 3 логических оператора:

  • И обозначается как &&;
  • ИЛИ обозначается как ||;
  • НЕ обозначается как !.

Операторы && и || помещаются между двумя значениями, например a && b или a || b. Оператор ! является префиксом, поэтому его необходимо поставить перед одним значением, например !a.

  • Выражение с оператором И возвращает true, когда оба его операнда имеют значение true. Когда любой из операндов имеет значение false, возвращается false;
  • Выражение с оператором ИЛИ возвращает true, когда один из операндов имеет значение true. Когда оба операнда являются false, возвращается false;
  • Выражение с оператором НЕ возвращает значение, противоположное текущему значению, поэтому true превращается false, а false становится true.

Больше про операторы можно узнать в отдельной статье. См. ссылку внизу.


Ветвления (Branches)

Для организации схем алгоритма с ветвлением в языке Swift используются условные операторы (Conditional Statements). Есть три условных оператора:

  • Оператор If (If Statement);
  • Оператор Switch (Switch Statement);
  • Оператор Guard (Guard Statement).

Условный оператор If (If Statement)

В Swift используется оператор if для запуска блочного кода только при выполнении определенного условия.

Например, выставление оценок (А, В, С) на основании оценок, полученных учащимся.

  • если процент выше 90, присвоить оценку A;
  • если процент выше 75, присвоить оценку B;
  • если процент выше 65, присвоить оценку C.

В Swift есть три формы оператора if.

  • if statement - отвечает за алгоритм ветвления 1;
  • if…else statement - отвечает за алгоритм ветвления 2;
  • if…else if…else statement - отвечает за алгоритм ветвления 3.

1. Swift If Statement

Синтаксис оператора if

Синтаксис оператора if в Swift:

if (condition) {
  // body of if statement
}

Оператор if оценивает условие внутри круглых скобок (condition).

  • Если условие (condition) оценивается как истинное (true), выполняется код внутри тела if;
  • Если условие (condition) оценивается как ложное (false), код внутри тела if пропускается.

Примечание. Код внутри { } является телом оператора if.

Схема работы оператора if и пример использования

Схема работы оператора if

Пример использования:

let number = 10

// check if number is greater than 0
if (number > 0) {
    print("Number is positive.")
}

print("The if statement is easy")

// Output
// Number is positive.
// The if statement is easy

В приведенном выше примере создали переменную с именем number. Обратите внимание на условие теста:

number > 0

Здесь, поскольку number больше 0, условие оценивается как истинное (true). Следовательно, код внутри тела if выполняется.

Если изменить переменную на отрицательное целое число. Скажем -5:

let number = -5

Теперь, когда мы запустим программу, вывод будет таким:

// The if statement is easy

Это связано с тем, что значение number меньше 0. Следовательно, условие оценивается как ложное (false). И тело блока if пропускается.

2. Swift If…else Statement

Синтаксис оператора if…else

Синтаксис оператора if...else в Swift:

if (condition) {
  // block of code if condition is true
} else {
  // block of code if condition is false
}

Оператор if...else оценивает условие внутри круглых скобок ().

  • Если условие (condition) оценивается как истинное (true):
    1. код внутри if выполняется;
    2. код внутри else пропускается;
  • Если условие (condition) оценивается как ложное (false):
    1. код внутри if пропускается;
    2. код внутри else выполняется.

Схема работы оператора if…else и пример использования

Схема работы оператора if...else

Пример использования:

let number = 10

if (number > 0) {
    print("Number is positive.")
} else {
    print("Number is negative.")
}

print("This statement is always executed.")

// Output
// Number is positive.
// This statement is always executed.

В приведенном выше примере создали переменную с именем number. Обратите внимание на условие теста:

number > 0

Здесь, поскольку number больше 0, условие оценивается как истинное (true). Следовательно, код внутри тела if выполняется.

Если изменить переменную на отрицательное целое число. Скажем -5:

let number = -5

Теперь, когда мы запустим программу, вывод будет таким:

// Number is negative.
// This statement is always executed.

Здесь тестовое выражение оценивается как ложное (false). Следовательно, выполняется код внутри тела else.

3. Swift if…else if…else Statement

Синтаксис оператора if…else if…else

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

Синтаксис оператора if...else if...else в Swift:

if (condition1) {
    // code block 1
} else if (condition2) {
    // code block 2
} else {
    // code block 3
}

В этом примере:

  • Если условие1 (condition1) оценивается как истинное (true), выполняется блок кода 1 (code block 1);
  • Если условие1 (condition1) оценивается как ложное (false), то оценивается условие2 (condition2):
    • Если условие2 (condition2) истинно (true), выполняется блок кода 2 (code block 2);
    • Если условие2 (condition2) ложно (false), выполняется блок кода 3 (code block 3).

Если необходимо больше вариантов в решении - можно добавлять необходимое количество блоков else if.

Схема работы оператора if…else if…else и пример использования

Схема работы оператора if...else if...else

Пример использования:

// check whether a number is positive, negative, or 0.

let number = 0

if (number > 0) {
    print("Number is positive.")
} else if (number < 0) {
    print("Number is negative")
} else {
    print("Number is 0.")
}

print("This statement is always executed")

// Output
// Number is 0.

В приведенном выше примере создали переменную с именем number со значением 0. Здесь есть два выражения условия:

  • if (number > 0) — проверяет, больше ли число 0;
  • else if (number < 0) — проверяет, меньше ли число 0.

Здесь оба условия оцениваются как ложные (false). Следовательно, выполняется код внутри тела else.

Вложенный оператор if

Можно использовать оператор if внутри оператора if. Это называется вложенным оператором if.

Синтаксис вложенного оператора if:

// outer if statement
if (condition1) {
    // statements

    // inner if statement
    if (condition2) {
    	// statements
    }
}

Заметки:

  • При необходимости можно добавить операторы else и else if во внутренний оператор if;
  • Мы также можем вставить внутренний оператор if во внешний оператор else или else if (если они существуют);
  • Можно вложить несколько слоев операторов if.

Синтаксис вложенного оператора if…else:

var number = 5

// outer if statement
if (number >= 0) {

    // inner if statement
    if (number == 0) {
        print("Number is 0")
    } else { // inner else statement
        print("Number is positive")
    }
} else { // outer else statement
    print("Number is negative")
}

// Output
// Number is positive

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

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

Тернарный оператор аналогичен простой конструкции if и имеет следующий синтаксис:

(condition) ? (code if true) : (code if false)

В зависимости от условия тернарный оператор возвращает второй или третий операнд:

  • если условие равно true, то возвращается второй операнд;
  • если условие равно false, то третий.

Например, такой код с оператором if-else:

var num1 = 10
var num2 = 6
var num3 = 0
if num1 > num2 {
	num3 = num1 - num2
} else {
	num3 = num1 + num2
}
print(num3)

// Output
// 4

Можно заменить на конструкцию с тернарным оператором:

var num1 = 10
var num2 = 6
var num3 = num1 > num2 ? num1 - num2 : num1 + num2
print(num3)

// Output
// 4

В данном случае num3 будет равно 4, так как num1 больше num2, поэтому будет выполняться второй операнд: num1 - num2.

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

Его удобно использовать для простой конструкции if-else. Использовать его для большего количества ветвлений возможно (подставляя во второй и третий операнд другие тернарные операторы), но тогда код становится сильно сложнее для чтения и понимания.

Сравните код с оператором if-else:

var num1 = 10
var num2 = 6
var num3 = 0
if num1 > num2 {
	if num1 > 0 {
		num3 = num1 - num2
	} else {
		num3 = 0
	}
} else {
	if num2 > 0 {
		num3 = num1 + num2
	} else {
		num3 = 0
	}
}
print(num3)

// Output
// 4

С кодом, который использует тернарный оператор:

var num1 = 10
var num2 = 6
var num3 = num1 > num2 ? num1 > 0 ? num1 - num2: 0 : num2 > 0 ? num1 + num2 : 0
print(num3)

// Output
// 4

Код с использованием тернарного оператора в этом случае - сложнее для восприятия.

Больше про операторы можно узнать в отдельной статье. См. ссылку внизу.

Условный оператор Switch (Switch Statement)

Оператор switch позволяет выполнить блок кода среди множества альтернатив.

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

Синтаксис оператора switch

Синтаксис оператора switch в Swift следующий:

switch (expression)  {
    case value1:
    	// statements 

    case value2:
        // statements 

    ...
    ...
        
    default:
        // statements
}

Оператор switch оценивает выражение в круглых скобках (expression):

  1. Если результат выражения равен значению1 (value1), выполняются выражения, которые указаны в операторе case value1:;
  2. Если результат выражения равен значению2 (value2), выполняются выражения, которые указаны в операторе case value2:;
  3. Если совпадения нет, выполняются выражения, которые указаны в операторе по умолчанию (default:).

Примечание: Можно сделать то же самое с конструкцией if...else...if. Однако синтаксис оператора switch чище, и его намного легче читать и писать.

Важно. Оператор switch должен иметь выражения на все случаи - Оператор switch должен быть исчерпывающим. Именно для этого указывается значения по умолчанию. Возможно не использовать default, если учтены все варианты. Например в перечислениях (Enum).

Схема работы оператора switch и пример использования

Схема работы оператора switch

Пример использования:

// program to find the day of the week 

let dayOfWeek = 4

switch dayOfWeek {
    case 1:
        print("Sunday")
	    
    case 2:
        print("Monday")
	    
    case 3:
        print("Tuesday")
	    
    case 4:
        print("Wednesday")
	    
    case 5:
        print("Thursday")
	    
    case 6:
        print("Friday")
	    
    case 7:
        print("Saturday")
	    
    default:
        print("Invalid day")
}

// Output
// Wednesday

В приведенном выше примере переменной dayOfWeek было присвоено значение 4. Теперь переменная сравнивается со значением каждого оператора case. Поскольку значение соответствует case 4, выполняется часть кода print("Wednesday") внутри case, и программа завершается.

В том случае, если переменной dayOfWeek будет присвоенно значение, которое не сооответствует ни одному case (например: -2, 0, 9, “23”) - будет выполнен код по умолчанию (print("Invalid day")).

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

Пример:

// program to find the day of the week 

let dayOfWeek = 4

switch dayOfWeek {
    case 1, 2, 3, 4, 5:
        print("I am working today")
	    
    case 6, 7:
        print("I am resting today")
	    
    default:
        print("Invalid day")
}

// Output
// I am working today

Использование Switch Statement с оператором передачи управления break

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

Например, можно указать оператор break в блоке default, если действие по умолчанию не предусмотрено.

Использование Switch Statement с оператором передачи управления fallthrough

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

Например:

let dayOfWeek = 4

switch dayOfWeek {
    case 1:
        print("Sunday")
	    
    case 2:
        print("Monday")
	    
    case 3:
        print("Tuesday")
	    
    case 4:
        print("Wednesday")
		fallthrough
	    
    case 5:
        print("Thursday")
	    
    case 6:
        print("Friday")
	    
    case 7:
        print("Saturday")
	    
    default:
        print("Invalid day")
}

// Output
// Wednesday
// Thursday

Switch Statement с диапазонами (Range)

Пример:

let ageGroup = 33

switch ageGroup {
    case 0...16:
        print("Child")

    case 17...30:
        print("Young Adults")

    case 31...45:
        print("Middle-aged Adults")

    default:
        print("Old-aged Adults")
}

// Output
// Middle-aged Adults

В приведенном выше примере вместо одного значения были использованы числовые диапазоны для каждого случая: case 0...16, case 17...30 и case 31...45. Здесь значение ageGroup равно 33, которое попадает в диапазон от 32 до 45. Следовательно, выполняется функция print("Middle-aged Adults").

Кортежи (Tuple) в Switch Statement

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

Например:

let info = ("Dwight", 38)

// match complete tuple values
switch info {
    case ("Dwight", 38): 
        print("Dwight is 38 years old")

    case ("Micheal", 46): 
        print("Micheal is 46 years old")

    default:
        print("Not known")
}

// Output
// Dwight is 38 years old

В приведенном выше примере был создан кортеж с именем info со значениями: "Dwight" и 38. Здесь вместо одного значения каждый оператор case также включает в себя кортежи: case ("Dwight", 38) и case ("Micheal", 46). Теперь значение info сравнивается с каждым оператором case. Поскольку значение совпадает с case ("Dwight", 38), выполняется функция print("Dwight is 38 years old").

Еще пример:

for i in 1...100 {
    switch (i % 3 == 0, i % 5 == 0) {
    case (true, false):
        print("Fizz")
    case (false, true):
        print("Buzz")
    case (true, true):
        print("FizzBuzz")
    default:
        print(i)
    }
}

В приведенном выше коде создается кортеж для проверки из двух значений: результат вычисления i % 3 == 0 и результат вычисления i % 5 == 0. Теперь можно учитывать различные значения кортежей: (true, false), (false, true), (true, true). Если ни один из этих случаев не совпадает, тогда выводится значение i.

Поскольку используем switch для проверки значений кортежа, то также можно проверять различные совпадающие значения в кортеже:

let airplane = (type: "737-800", heading: "LAX")
 
switch airplane {
	case ("737-800", _):
    	print("Этот самолет модели 737-800.")
	case (let type, "LAX"):
    	print("Этот \(type) летит в аэропорт Лос-Анджелеса.")
	default:
    	print("Неизвестный самолет.")
}

// Output
// Этот самолет модели 737-800.

В приведенном выше коде рассмотрено 3 разных случая для константы airplane, которая представляет собой кортеж по типу (String, String):

  • ("737-800", _) - первое значение совпадает, второе значение не рассматривается, может быть любым;
  • (let type, "LAX") - первое значение связывается с константой type, но не проверяется в условии, второе значение совпадает;
  • default - остальные случаи.

Выражение let type называется связыванием значения. Это позволяет привязать значение кортежа к константе. Теперь внутри блока case можно использовать эту константу, например, распечатать ее.

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

Пример:

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")
}

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

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

Пример:

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")
}

// Output
// On an axis, 9 from the origin

Кейс выше имеет два шаблона: (let distance, 0), который соответсвует любой точке на оси “x”, и (0, let distance), что соответствует точке на оси “y”. И тот и другой шаблон включают в себя привязку для distance и distance является целочисленным значением для двух этих шаблонов, что значит, что код внутри тела кейса всегда будет иметь доступ к значению distance.

Больше о кортежах (tuples) можно узнать в отдельной статье. См. ссылку внизу.

Использование switch с оговоркой where

Также можно использовать switch с оператором where:

enum Response {
    case error(Int, String)
    case success
}
 
let httpResponse = Response.success
 
switch httpResponse {
	case .error(let code, let status) where code > 399:
    	print("HTTP Ошибка: \(code) \(status)")
	case .error:
    	print("HTTP запрос неуспешен")
	case .success:
    	print("HTTP запрос успешен")
}

// Output
// HTTP запрос успешен

В приведенном коде происходят две вещи:

  • Создаем перечисление Response, которое имеет два случая: .error и .success. Случай .error имеет два соответствующие значения: целое число и строку;
  • Программа оценивает значение httpResponse с помощью оператора switch, реагируя на разные значения перечисления Response.

Если изменить значение httpResponse:

let httpResponse = Response.error(404, "Not Found")

// Output
// HTTP Ошибка: 404 Not Found

Или:

let httpResponse = Response.error(301, "Moved Permanently")

// Output
// HTTP запрос неуспешен

Есть еще один .error случай, который нас особенно интересует, а именно код ошибки, который больше, чем 399.

Коды состояния HTTP, которые используются в интернете, указывают на то, что коды состояния от 400 и выше являются ошибками клиента и сервера. Иными словами, когда получаете один из них - то тогда что-то пошло не так.

В приведенном выше коде используется привязка значений для констант code и status соответствующих значений перечисления Response. Также используется ключевое слово where, чтобы указать, что этот случай должен выполняться для любого значения code, который превышает значение 399.

Использование switch с перечислениями

Перечисления имеют схожий синтаксис с оператором switch.

Рассмотрим следующее перечисление:

enum Compass {
    case north
    case east
    case south
    case west
}

Если необходимо найти нужное направление, можно использовать следующий код:

let heading = Compass.south
 
if heading == .north {
    print("Вы направляетесь на север!")
} else if heading == .east {
    print("Вы направляетесь на восток!")
} else if heading == .south {
    print("Вы направляетесь на юг!")
} else if heading == .west {
    print("Вы направляетесь на запад!")
}

Приведенный выше код использует оператор if для оценки значения heading и вывода соответствующей строки текста.

Можно сделать то же самое, используя оператор switch:

switch heading {
case .north:
    print("Вы направляетесь на север!")
case .east:
    print("Вы направляетесь на восток!")
case .south:
    print("Вы направляетесь на юг!")
case .west:
    print("Вы направляетесь на запад!")
}

Сначала используется ключевое слово switch, а затем проверяемое выражение, в данном случае это константа heading. Это значение, которое рассматривается блоками switch. Затем перебираются возможные варианты значений с помощью case. В приведенном выше примере - рассматриваются все возможные значения перечисления Compass.

Так как все варианты в перечислении указаны, в блоке switch можно не указывать блок default.

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

switch heading {
case .north:
    print("Вы направляетесь на север!")
case .south:
    print("Вы направляетесь на юг!")
case .west:
    print("Вы направляетесь на запад!")
}

// Output
// error: switch must be exhaustive

В этом примере появляется ошибка, которая сообщает, что оператор switch должен быть исчерпывающим.

Особенности работы с оператором switch

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

Рассмотрим перечисление Authorization:

enum Authorization {
    case granted
    case undetermined
    case unauthorized
    case denied
    case restricted
}

Если необходимо предоставить пользователю доступ к ресурсу в зависимости от его состояния авторизации, можно сделать это:

switch state {
	case .granted:
    	print("Доступ разрешен.")
	default:
    	print("Доступ закрыт!")
}

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

Также можно проверить более сложные случаи:

switch state {
	case .granted:
    	print("Доступ разрешен.")
	case .undetermined:
    	print("Предоставьте код доступа.")
	case .unauthorized, .denied, .restricted:
    	print("Доступ закрыт!")
}

Последний случай объединяет несколько случаев в одной строке.

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

var message = "Ответ: "
let state = Authorization.undetermined
 
switch state {
	case .granted:
    	message += "Доступ открыт. Вы можете продолжить"
	case .undetermined:
    	message += "Предоставьте код доступа."
    	fallthrough
	default:
    	message += "Доступ закрыт!"
}
 
print(message)

// Output
// Ответ: Предоставьте код доступа. Доступ закрыт!

В приведенном выше примере выполняется как условие .undetermined, так и default.

Условный оператор Guard (Guard Statement)

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

Оператор guard похож на оператор if с одним существенным отличием. Оператор if запускается, когда выполняется определенное условие. Однако оператор guard запускается, когда определенное условие не выполняется.

Синтаксис оператора guard

Синтаксис оператора guard:

guard expression else {
	// statements
	// control statement: return, break, continue or throw.
}

Здесь выражение (expression) возвращает либо истину (true), либо ложь (false). Если выражение оценивается как

  • true - операторы внутри блока кода guard не выполняются;
  • false - операторы внутри блока кода guard выполняются.

Примечание. Необходимо использовать операторы передачи управления: return, break, continue или throw для выхода из области видимости guard.

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

Схема работы оператора guard и пример использования

Схема работы оператора guard

Пример использования:

var i = 2

while (i <= 10) {
    // guard condition to check the even number 
    guard i % 2 == 0 else {
        i = i + 1
        continue
    }

    print(i)
    i = i + 1
}
// Output
// 2
// 4
// 6
// 8
// 10

В приведенном выше примере использовался оператор guard, чтобы проверить, является ли число четным или нечетным. Обратите внимание на строку:

guard i % 2 == 0 else {...}

Здесь, если i

  • нечетное, то выражение i % 2 == 0 оценивается как ложное (false). И код внутри guard выполняется;
  • чётное, то выражение i % 2 == 0 оценивается как правдивое (true). И код внутри guard пропускается.

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

Использование управляющих операторов, таких как continue, break и т.д., является обязательным. В противном случае - получим ошибку:

‘guard’ body must not fall through, consider using a ‘continue’ or ‘return’ to exit the scope

Оператор guard внутри функции

Оператор Guard обычно используется с функциями. Функция — это блок кода, выполняющий определенную задачу. Чтобы узнать больше, посетите Swift Function. Давайте посмотрим, как мы можем использовать оператор защиты с функциями.

Пример:

// create a function
func checkOddEven(number: Int) {

    // use of guard statement
    guard number % 2 == 0 else {
        print("Odd Number")
        return
    }

    print("Even Number")
}

// function call
checkOddEven(number: 3)

// Output
// Odd Number

В приведенном выше примере создали функцию с именем checkOddEven(number: Int). Обратите внимание на использование оператора guard внутри функции.

Здесь оператор защиты проверяет, является ли число четным или нечетным. Поскольку число нечетное, условие number % 2 == 0 возвращает false (оценивается как ложное). Следовательно, код внутри оператора guard выполняется.

Теперь изменим значение число в вызове функции на четное число, например, 4:

// function call
checkOddEven(number: 4)

// Output
// Even Number

Здесь, поскольку число четное, условия number % 2 == 0 возвращает true (оценивается как истинное). Таким образом, код внутри оператора guard пропускается, а другой код внутри функции выполняется. Следовательно, получаем четное число в качестве вывода.

Guard с несколькими условиями

Операторы guard также может объединять несколько условий, разделенных запятой (,):

func checkJobEligibility(age: Int) {

    guard age >= 18, age <= 40 else {
      print("Not Eligible for Job")
      return
    }

    print("You are eligible for this job")
}

// function call
checkJobEligibility(age: 33)

// Output
// You are eligible for this job

В приведенном выше примере оператор guard содержит два условия, разделенных запятой. Здесь будет “Логическое И” между двумя условиями. То есть guard условие истинно, только если оба условия true.

Guard-let Statement

При работе с опционалами Swift оператор guard используется как оператор guard-let.

Например:

func checkAge(age: Int?) {

    guard let myAge = age else {
      print("Age is undefined")
      return
    }

    print("My age is \(myAge)")
}

// function call
checkAge(age: 22)

// Output
// My age is 22

В приведенном выше примере используется оператор guard-let, чтобы проверить, содержит ли входящий параметр функции age значение или нет. Поскольку age содержит некоторое значение, код внутри блока guard-let не выполняется. Это работает так же, как если бы условие guard было истинным.

Guard Statement или If Statement

Оператор guard вводится как альтернатива оператору if. Например, предположим, необходимо проверить, имеет ли человек право голосовать. Можно использовать оператор if:

func voteEligibility(age: Int) {

    if age >= 18 {
        print("Eligible to vote")
    } else {
        print("Not eligible to vote")
    }
}

// function call
voteEligibility(age: 42)

// Output
// Eligible to vote

Эту же программу можно написать с помощью оператора guard:

func voteEligibility(age: Int) {

    guard age >= 18 else {
        print("Not Eligible to vote")
        return
    }

    print("Eligible to vote")
}

// function call
voteEligibility(age: 42)

// Output
// Eligible to vote

Как видно из примера, с оператором guard можно выйти из функции, как только условие будет оценено как ложное.

Оператор guard удобно использовать в случае когда код необходимо выполнять только в случае, если условие выполняется, а в случае невыполнения условия - прекратить выполнение функции и выйти из нее. Например, код должен выполняться только при подключению к серверу. В противном случае - код не должен выполнятся и работа функции должна быть остановлена. Можно в этом случае использовать и оператор if, но тогда весь код бы находился внутри блока if. Использование guard делает код удобнее для чтения и написания, а также помогает избежать дополнительной вложенности.

Ранний выход (Early Exit)

Инструкция guard, как и инструкция if, выполняет выражения в зависимости от логического значения условия. Можно использовать guard, чтобы указать на то, что условие обязательно должно быть true, чтобы код после самой инструкции guard выполнился. В отличии от инструкции if, guard всегда имеет код внутри else, который выполняется, когда условие оценивается как false.

Пример:

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"])

// Output
// Hello John!
// I hope the weather is nice near you.

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

// Output
// Hello Jane!
// I hope the weather is nice in Cupertino.

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

Если условие не выполняется, то исполняется код внутри else. Эта ветка должна перебросить исполнение кода на выход из этого блока кода, в котором был определен guard. А сделать это можно при помощи инструкций return, break, continue, throw или можно вызвать метод, который ничего не возвращает, например fatalError(_file:line:).

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


Циклы (Loops)

Для организации схем алгоритма с повторениями (циклами) в языке Swift используются операторы цикла. Есть три оператора цикла:

  • Оператор цикла For-In (For-In Loops);
  • Операторы цикла While (While Loops):
    • Оператор цикла While;
    • Оператор цикла Repeat-While.

Итерация - это повторение внутри цикла; количество итераций - количество раз, которое повторится тело цикла.

Цикл For-In (For-In Loops)

В Swift цикл for-in используется для запуска блока кода определенное количество раз. Он используется для перебора любых последовательностей, таких как массив, диапазон, строка и т.д.

Синтаксис цикла for-in:

for val in sequence {
	// statements
}

Здесь val обращается к каждому элементу последовательности (sequence) на каждой итерации. Цикл продолжается до тех пор, пока мы не достигнем последнего элемента в последовательности (sequence).

val является константой и может применяться в локальной области видимости цикла - соответсвенно ее значение внутри области видимости цикла нельзя изменить. val принимает для каждой итерации значение по очереди из последовательности.

Если значение val внутри цикла не используется, то необходимо ее имя заменить на символ нижнего подчеркивания (_). Например, цикл необходимо выполнить несколько раз, без применения значения данной константы:

for _ in 1...3 {
	print("Hello")
}

// Output
// Hello
// Hello
// Hello

Цикл выполнился три раза - по очереди перебрались значения диапазона: 1, 2, 3.

Схема работы цикла for-in и пример использования

Схема работы цикла for-in

Пример использования:

// access items of an array 
let languages = ["Swift", "Java", "Go", "JavaScript"]

for language in languages {
    print(language)
}

// Output
// Swift
// Java
// Go
// JavaScript

В приведенном выше примере создали массив с именем languages.

Изначально значение language устанавливается как первый элемент массива, т.е. Swift, поэтому внутри цикла выполняется функция print() с этим значением для константы language.

В следующей итерации language обновляется со следующим элементом массива, и функция print() выполняется снова. Таким образом, цикл выполняется до тех пор, пока не будет получен доступ к последнему элементу массива.

Цикл for-in с оговоркой where

В Swift также можно добавить оговорку where с циклом for-in. Такая конструкция используется для реализации фильтров в цикле. То есть, если условие в оговорке where возвращает true, тогда цикл выполняется.

Пример:

// remove Java from an array

let languages = ["Swift", "Java", "Go", "JavaScript"]

for language in languages where language != "Java"{
    print(language) 
}

// Output
// Swift
// Go
// JavaScript

В приведенном выше примере использовался цикл for-in для доступа к каждому элементу languages. Обратите внимание на использование оговорки where в цикле for-in:

for language in languages where language != "Java"

Здесь, если условие в оговорке where возвращает true, выполняется код внутри цикла. В противном случае он не выполняется.

Итерация language condition: != “Java” Output
1 Swift true Swift
2 Java false No Output
3 Go true Go
4 JavaScript true JavaScript

Цикл for-in с диапазонами

Диапазон представляет собой ряд значений между двумя числовыми интервалами.

Например:

var values = 1...3

Здесь 1...3 определяет диапазон, содержащий значения 1, 2, 3. В Swift можно использовать цикл for-in для перебора диапазона.

Например:

// iterate from i = 1 to 1 = 3
for i in 1...3 {
    print(i)
}

// Output
// 1
// 2
// 3

В приведенном выше примере мы использовали цикл for-in для итерации в диапазоне от 1 до 3.

Значение i устанавливается равным 1 и обновляется до следующего числа диапазона на каждой итерации. Этот процесс продолжается до тех пор, пока не будет достигнуто значение 3.

Итерация Условие Действие
1 true печатается 1, i увеличена до 2
2 true печатается 2, i увеличена до 3
3 true печатается 3, i увеличена до 4
4 false Выполнение цикла остановлено

Аналогично цикл работает с диапазонами, которые начинаются не с 1, а другого числа.

Примечание. Диапазон представлен числами с типом данных Int. Поскольку типы с плавающей запятой, такие как Float и Double, являются собственными типами Stride, их нельзя использовать в качестве границ счетного диапазона. Если вам нужно выполнить итерацию по последовательным значениям с плавающей запятой, см. функцию stride(from:to:by:).

Цикл for-in с функцией stride(from:to:by)

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

Например:

for i in stride(from: 1, to: 10, by: 2) {
    print(i)
}

// Output
// 1
// 3
// 5
// 7
// 9

В приведенном выше примере цикл повторяется в диапазоне от 1 до 10 с интервалом 2. Однако 10 не включается в последовательность. Здесь значение i установлено равным 1 и обновляется с интервалом 2 на каждой итерации.

Больше о функции stride(from:to:by) можно узнать в отдельной статье. См. ссылку внизу.

Циклы While и Repeat-While

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

  • Цикл While - отвечает за алгоритм цикла “Пока”;
  • Цикл Repeat-While - отвечает за алгоритм цикла “До”.

Важно. В этих циклах необходимая составляющая - это блок условий выполнения цикла. Если этот блок прописать некорректно, то цикл может выполняться бесконечно. Это приведет к постоянно повышающимися затратами ресурсов и, в конечном итоге, “зависанию” программы.

Цикл While

Цикл while используется для запуска определенного кода до тех пор, пока не будет выполнено определенное условие.

Синтаксис цикла while:

while (condition){
  // body of loop
}

Здесь,

  • Цикл while оценивает условие внутри круглых скобок (condition);
  • Если условие оценивается как истинное (true), выполняется код внутри цикла while;
  • Состояние оценивается повторно;
  • Этот процесс продолжается до тех пор, пока условие не станет ложным (false);
  • Когда условие оценивается как ложное (false), цикл останавливается.

Схема работы цикла while и пример использования

Схема работы цикла while

Пример использования:

// program to display numbers from 1 to 5

// initialize the variable
var i = 1, n = 5

// while loop from i = 1 to 5
while (i <= n) {
    print(i)
    i = i + 1
}

// Output
// 1
// 2
// 3
// 4
// 5

Цикл проверяет условие i <= n и если оно выполняется, то выполняется код в теле цикла и условие проверяется. Внутри цикла организовано изменение переменной i - это позволяет выйти из цикла при достижении i такого значения, когда условие цикла перестанет выполняться.

Следующий пример:

var currentLevel:Int = 0, finalLevel:Int = 5
let gameCompleted = true
while (currentLevel <= finalLevel) {

  if gameCompleted {
    print("You have passed level \(currentLevel)")
      currentLevel += 1
  }
}
print("Level Ends")

// Output
// You have passed level 0                                                                                                                 
// You have passed level 1                                                                                                                 
// You have passed level 2                                                                                                                 
// You have passed level 3                                                                                                                 
// You have passed level 4                                                                                                                 
// You have passed level 5                                                                                                                 
// Level Ends     

В приведенном выше примере использовался цикл while для проверки текущего уровня и отображения его на консоли.

Цикл Repeat-While

Цикл repeat-while похож на цикл while с одним ключевым отличием. Тело цикла repeat-while выполняется один раз перед проверкой тестового выражения.

Синтаксис цикла repeat-while:

repeat {
  // body of loop
} while (condition)   

Здесь,

  • Сначала выполняется тело цикла. Затем оценивается состояние (condition);
  • Если условие оценивается как истинное (true), тело цикла внутри оператора repeat выполняется снова;
  • Состояние (condition) оценивается еще раз;
  • Этот процесс продолжается до тех пор, пока условие не станет ложным (false). Затем цикл останавливается.

Схема работы цикла repeat-while и пример использования

Схема работы цикла repeat-while

Пример использования:

// program to display numbers

var i = 1, n = 5

// repeat...while loop from 1 to 5
repeat {
    print(i)
    i = i + 1
} while (i <= n)

// Output
// 1
// 2
// 3
// 4
// 5
// 6

Цикл выполняет код внутри тела цикла и затем проверяет условие i <= n. И если оно выполняется, то снова выполняется код в теле цикла и условие проверяется опять. Внутри цикла организовано изменение переменной i - это позволяет выйти из цикла при достижении i такого значения, когда условие цикла перестанет выполняться.

В отличии от цикла while, цикл repeat-while сначала выполняет код и затем проверяет условие. Таким образом этот цикл выполнит на одну итерацию больше, по сравнению с циклом while.

Бесконечный цикл while

Если условие цикла while всегда истинно, цикл выполняется бесконечное количество раз (пока не заполнится память). Это называется бесконечным циклом while.

Например:

while (true) {
    print("Endless Loop")
}

// Output
// Endless Loop
// Endless Loop 
// ...
// ...
// ...

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

Цикл for-in или цикл while

Цикл for-in обычно используется, когда известно количество итераций.

Например:

// this loop is iterated 5 times
for number in 1...5 {
   // body of loop
}

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

Например:

while (condition) {
    // body of loop
}

Циклом for-in удобнее перебирать коллекции, так как в каждой итерации сразу можно получить значение элемента коллекции. Но его так же можно заменить циклом while.

Пример:

// access items of an array 
let languages = ["Swift", "Java", "Go", "JavaScript"]
var i = 0

while i < languages.count {
	print(languages[i])
	i += 1
}

// Output
// Swift
// Java
// Go
// JavaScript

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

Примечание. Работа цикла repeat-while такая же, как и цикла while. Следовательно, он также используется, когда количество итераций неизвестно.

Вложенные циклы

Если цикл существует внутри тела другого цикла, он называется вложенным циклом. Вот пример вложенного цикла for-in.

Пример:

// outer loop
for i in 1...5 {
    // codes

    // inner loop
    for j in 1...2 {
        //codes
    }
}

Здесь внутренний цикл for j in 1...2 вложен во внешний цикл for i in 1...5.

Вложенный цикл в цикл for-in

Вложенный цикл for-in включает один цикл for-in внутри другого цикла for-in.

Например:

// Swift program to display 7 days of 2 weeks

// outer loop
for week in 1...2 {
    print("Week: \(week)")

    // inner loop
    for day in 1...7 {
        print("  Day:  \(day)")
    }

    // line break after iteration of outer loop
    print("")
}

// Output
// Week: 1
//   Day:  1
//   Day:  2
// ...
//
// Week: 2
//   Day:  1
//   Day:  2
// ...

В приведенном выше примере внешний цикл повторяется 2 раза и печатает 2 недели: Week: 1 и Week: 2. И внутренний цикл повторяется 7 раз и печатает 7 дней: Day: 1, Day: 2 и так далее.

Примечание. Точно так же мы можем создавать вложенные циклы while и repeat-while.

Например:

// outer while loop
while (condition1) {
    ...

    // inner while loop
    while (condition2) {
        ...
    }
}

Вложенный цикл в for-in цикл while

Также можно создавать вложенные циклы разных типов. То есть можно поместить цикл for-in внутрь цикла while и наоборот.

Например:

// program to display 7 days of 2 weeks
var weeks = 2
var i = 1

// outer while loop
while (i <= weeks) {
    print("Week: \(i)")

    // inner for loop
    for day in 1...7 {
        print("  Day:  \(day)")
    }
    i = i + 1
}

// Output
// Week: 1
//   Day:  1
//   Day:  2
// ...
//
// Week: 2
//   Day:  1
//   Day:  2
// ...

Здесь использовался цикл for-in внутри цикла while.

Примечание. Точно так же можно вложить цикл repeat-while внутрь цикла while или for-in.

Операторы передачи управления break и continue внутри вложенного цикла

1. break внутри вложенного цикла

Когда мы используем оператор break внутри внутреннего цикла, он завершает внутренний цикл, но не внешний цикл.

Например:

// outer loop
for week in 1...3 {
    print("Week: \(week)")

    // inner loop
    for day in 1...7 {
       if (week == 2) {
          // use of break statement
          break
       }
       print("  Day:  \(day)")
   }

   print("")
}

// Output
// Week: 1
//   Day:  1
//   Day:  2
// ...
//
// Week: 2
//
// Week: 3
//   Day:  1
//   Day:  2
// ...

В приведенном выше примере использовался оператор break внутри внутреннего цикла for-in.

Здесь программа завершает цикл, когда week равна 2. Следовательно, дни week 2 не печатаются. Однако внешний цикл, который печатает неделю, не затрагивается.

2. continue внутри вложенного цикла

Точно так же, когда мы используем оператор continue внутри внутреннего цикла, он пропускает только текущую итерацию внутреннего цикла.

Например:

// outer loop
for week in 1...2 {
    print("Week: \(week)")

    // inner loop
    for day in 1...7 {

        // use of continue statement
        if (day % 2 != 0) {
            continue
        }

        print("  Day:  \(day)")
    }

    print("")
}

// Output
// Week: 1
//   Day:  2
//   Day:  4
//   Day:  6
//
// Week: 2
//   Day:  2
//   Day:  4
//   Day:  6

В приведенном выше примере использовался оператор continue внутри внутреннего цикла for-in.

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


Операторы передачи управления (Control Transfer Statements)

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

  • continue;
  • break;
  • fallthrough;
  • return;
  • throw;

Операторы continue, break, fallthrough описаны ниже. Оператор return описан в статье “Функции” (см. ссылку внизу), а оператор throw описан в статье “Передача и обработка ошибок” (см. ссылку внизу).

Оператор continue (continue Statement)

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

Синтаксис оператора continue следующий:

continue

Схема работы оператора continue и пример использования

Схема работы оператора continue

Оператор continue и цикл for-in

Можно использовать оператор continue с циклом for-in, чтобы пропустить текущую итерацию цикла. Затем управление программой переходит к следующей итерации.

Например:

for i in 1...5 {
  
    if i == 3 {
        continue
    }
 
    print(i)
}

// Output
// 1
// 2
// 4
// 5

Здесь, когда i равно 3, выполняется оператор continue. Следовательно, значение 3 не печатается на выходе.

Оператор continue и цикл while

Можно также пропустить текущую итерацию цикла while, используя оператор continue.

Например:

// program to print odd numbers from 1 to 10

var num = 0

while num <= 10{
    num += 1

    if (num % 2) == 0 {
        continue
    }

    print("\(num)")
}

// Output
// 1
// 3
// 5
// 7
// 9

В приведенном выше примере использовали цикл while для вывода нечетных чисел от 1 до 10.

Здесь, когда число четное, оператор continue пропускает текущую итерацию и начинает следующую итерацию.

Оператор continue и вложенные циклы (nested loops)

Когда используем оператор continue с вложенными циклами, он пропускает текущую итерацию внутреннего цикла.

Например:

for i in 1...3 {
    for j in 1...3 {
    
        if j == 2 {
            continue
        }
    
        print("i = \(i), j = \(j)")
    }
}

// Output
// i = 1, j = 1
// i = 1, j = 3
// i = 2, j = 1
// i = 2, j = 3
// i = 3, j = 1
// i = 3, j = 3

В этом примере, когда значение j равно 2, выполняется оператор continue. Следовательно, значение j = 2 никогда не отображается в выходных данных.

Маркированный оператор continue (Labeled continue)

До сих пор использовался немаркированный оператор continue. Однако в Swift есть еще одна форма оператора continue, известная как labeled continue.

При использовании вложенных циклов мы можем пропустить текущую итерацию внешнего цикла с помощью оператора labeled continue.

Схема работы оператора labeled continue

Как видно на изображении выше, использовался идентификатор outerloop внешнего цикла для указания внешнего цикла. Теперь обратите внимание, как используется оператор continue (continue externalloop).

Здесь оператор continue пропускает текущую итерацию помеченного цикла (outerloop). Затем управление программой переходит к следующей итерации помеченного цикла.

Пример Labeled continue:

outerloop: for i in 1...3 {
  
    innerloop: for j in 1...3 {
    
        if j == 3 {
            continue outerloop
        }
    
        print("i = \(i), j = \(j)")
    }
}

// Output
// i = 1, j = 1                                                                                                                            
// i = 2, j = 1                                                                                                                            
// i = 3, j = 1   

В приведенном выше примере обозначили циклы как:

  • outerloop: for i in 1…3 {…};
  • innerloop: for j in 1…3 {…}.

Это помогает идентифицировать петли. Обратите внимание на использование помеченного оператора continue:

if j == 3 {
    continue outerloop
}

Здесь оператор continue пропускает итерацию внешнего цикла, помеченного как outerloop и переходит к следующей итерации, когда значение j равно 3.

Примечание. Частое использование labeled continue не рекомендуется, так как это затрудняет понимание кода.

Оператор break (break Statement)

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

Синтаксис оператора break следующий:

break

Схема работы оператора break и пример использования

Схема работы оператора break

Оператор break и цикл for-in

Можно использовать оператор break с циклом for-in, чтобы завершить цикл при выполнении определенного условия.

Например:

for i in 1...5 {
  
    if i == 3 {
        break
    }
 
    print(i)
}

// Output
// 1
// 2

Здесь, когда i равно 3, выполняется оператор break. После этого в выводе не будет ничего после значения 2.

Оператор break и цикл while

Можно также прекратить выполнение цикла while, используя оператор break.

Например:

// program to find first 5 multiples of 6

var i = 1

while (i <= 10) {
    print("6 * \(i) =",6 * i)

    if i >= 5 {
        break
    }
 
    i = i + 1
}

// Output
// 6 * 1 = 6
// 6 * 2 = 12
// 6 * 3 = 18
// 6 * 4 = 24
// 6 * 5 = 30

В приведенном выше примере использовали цикл while для вывода произведения первых пчти чисел и 6.

Здесь, когда i становиться больше 5 - выполнение цикла останавливается.

Оператор break и вложенные циклы (nested loops)

Оператор break можно использовать с вложенными циклами:

  • если использовать оператор break внутри внутреннего цикла, то тогда внутренний цикл останавливается;
  • если использовать оператор break снаружи внутреннего цикла, то тогда внешний цикл останавливается.

Например:

// outer for loop
for i in 1...3 {

    // inner for loop
    for j in 1...3 {

        if i == 2 {
            break
        }

        print("i = \(i), j = \(j)")
    }
}

// Output
// i = 1, j = 1                                                                                                                            
// i = 1, j = 2                                                                                                                            
// i = 1, j = 3                                                                                                                            
// i = 3, j = 1                                                                                                                            
// i = 3, j = 2                                                                                                                            
// i = 3, j = 3 

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

Маркированный оператор break (Labeled break)

До сих пор использовался немаркированный оператор break. Однако в Swift есть еще одна форма оператора break, известная как labeled break.

При использовании вложенных циклов мы можем прекратить выполнение внешнего цикла с помощью оператора labeled break.

Схема работы оператора labeled break

Как видно на изображении выше, использовался идентификатор outerloop внешнего цикла для указания внешнего цикла. Теперь обратите внимание, как используется оператор break (break outerloop).

Здесь оператор break обрывает выполнение помеченного цикла (outerloop). Затем управление программой переходит к следующему оператору, который следует за помеченным циклом.

Пример Labeled break:

outerloop: for i in 1...3 {

    innerloop: for j in 1...3 {

        if j == 3 {
            break outerloop
        }

        print("i = \(i), j = \(j)")
    }
}

// Output
// i = 1, j = 1
// i = 1, j = 2 

В приведенном выше примере обозначили циклы как:

  • outerloop: for i in 1…3 {…};
  • innerloop: for j in 1…3 {…}.

Это помогает идентифицировать петли. Обратите внимание на использование помеченного оператора break:

if j == 3 {
  break outerloop
}

Здесь оператор break останавливает выполнение внешнего цикла, помеченного как outerloop и выходит из него, когда значение j равно 3.

Примечание. Частое использование labeled break не рекомендуется, так как это затрудняет понимание кода.

Оператор fallthrough (fallthrough Statement)

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

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

Если по какой-то причине необходимо аналогичное “проваливание” как в языке 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)

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

В этом примере константа integerToDescribe совпадает с условием кейса, поэтому программа выполняет код внутри. Потом программа встречает fallthrough и переходит к выполнению кода в следующем блоке - default.

Если бы константа integerToDescribe не совпала с условием кейса, то произошел бы переход к блоку default и выполнение его кода.

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

Проверка доступности API (Checking API Availability)

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

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

Синтаксис такой:

if #available (название платформы версия платформы, ..., * ) {
	// выражения для исполнения, если соответствующие условию API доступны
} else {
	// выражения для исполнения, если соответствующие условию API не доступны
}

Пример:

if #available(iOS 10, macOS 10.12, *) {
    // Используйте API iOS 10 для iOS и используйте API macOS 10.12 на macOS
} else {
    // Используйте более старые API для iOS и macOS
}

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


Еще полезные ссылки

Также информацию по управлению потоком можно получить на странице официальной документации.

Ссылки на официальную документацию: