Видеокурсы по изучению языка программирования Swift. Подробнее

Замыкающие выражения

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

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

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

Метод sorted

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

Примеры замыкающих выражений ниже используют метод sorted(by:)для сортировки массива из String значений в обратном алфавитном порядке. Вот исходный массив для сортировки:

let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

Метод sorted(by:) принимает два аргумента одного и того же типа, что и содержимое массива, и возвращает Bool значение, которое решает поставить ли первое значение перед вторым, или после второго. Замыкание сортировки должно вернуть true, если первое значение должно быть до второго значения, и false в противном случае.

Этот пример сортирует массив из String значений, так что сортирующее замыкание должно быть функцией с типом (String, String) -> Bool.

Один из способов обеспечить сортирующее замыкание, это написать нормальную функцию нужного типа, и передать его в качестве аргумента метода sorted(by:):

func backward(_ s1: String, _ s2: String) -> Bool {
   return s1 > s2
}
var reversedNames = names.sorted(by: backward)
// reversedNames равен ["Ewa", "Daniella", "Chris", "Barry", "Alex"]

Если первая строка (s1) больше чем вторая строка (s2), функция backwards(_:_:) возвращает true, что указывает, что s1 должна быть перед s2 в сортированном массиве. Для символов в строках, "больше чем" означает "появляется в алфавите позже, чем". Это означает что буква "B" "больше чем" буква "А", а строка "Tom" больше чем строка "Tim". Это делает обратную алфавитную сортировку, с "Barry" поставленным перед "Alex", и так далее.

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

Синтаксис замыкающего выражения

Синтаксис замыкающего выражения имеет следующую общую форму:

  1. { (параметры) -> тип результата in
  2. выражения
  3. }

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

Пример ниже показывает версию функции backwards(_:_:) с использованием замыкающего выражения:

reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
   return s1 > s2
})

Обратите внимание, что объявление типов параметров и типа возвращаемого значения для этого однострочного замыкания идентично объявлению из функции backwards(_:_:). В обоих случаях, оно пишется в виде (s1: String, s2: String) -> Bool. Тем не менее, для однострочных замыкающих выражений, параметры и тип возвращаемого значения пишутся внутри фигурных скобок, а не вне их.

Начало тела замыкания содержит ключевое слово in. Это ключевое слово указывает, что объявление параметров и возвращаемого значения замыкания закончено, и тело замыкание вот-вот начнется.

Поскольку тело замыкания настолько короткое, оно может быть записано в одну строку:

reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 })

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

Вывод типа из контекста

Поскольку сортирующее замыкание передается как аргумент метода, Swift может вывести типы его параметров и тип возвращающевомого значения, через тип параметра метода sorted(by:). Этот параметр ожидает функцию имеющую тип (String, String) -> Bool. Это означает что типы (String, String) и Bool не нужно писать в объявлении замыкающего выражения. Поскольку все типы могут быть выведены, стрелка результата ( -> ) и скобки вокруг имен параметров также могут быть опущены:

reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 })

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

Тем не менее, вы всё равно можете явно указать типы, если хотите. И делать это предполагается, если это поможет избежать двусмысленности для читателей вашего кода. В случае с методом sorted(by:), цель замыкания понятна из того факта, что сортировка происходит, и она безопасна для читателя, который может предположить, что замыкание, вероятно, будет работать со значениями String, поскольку оно помогает сортировать массив из строк.

Неявные возвращаемые значения из замыканий с одним выражением

Замыкания с одним выражением могут неявно возвращать результат своего выражения через опускание ключевого слова return из их объявления, как показано в этой версии предыдущего примера:

reversedNames = names.sorted(by: { s1, s2 in s1 > s2 })

Здесь, функциональный тип аргумента метода sorted(by:)дает понять, что замыкание вернет Bool значение. Поскольку тело замыкания содержит одно выражение (s1 > s2), которое возвращает Bool значение, то нет никакой двусмысленности, и ключевое слово return можно опустить.

Сокращенные имена параметров

Swift автоматические предоставляет сокращённые имена для однострочных замыканий, которые могут быть использованы для обращения к значениям параметров замыкания через имена $0, $1, $2, и так далее.

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

reversedNames = names.sorted(by: { $0 > $1 })

Здесь, $0 и $1 обращаются к первому и второму String параметру замыкания.

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

Здесь есть на самом деле более короткий способ написать замыкающее выражение выше. Тип String в Swift определяет свою специфичную для строк реализацию оператора больше ( > ) как функции, имеющей два строковых параметра и возвращающей значение типа Bool. Это точно соответствует типу метода, для параметра метода sorted(by:). Таким образом, вы можете просто написать оператор больше, а Swift будет считать, что вы хотите использовать специфичную для строк реализацию:

reversed = names.sorted(by: >)

Более подробную информацию о операторных функциях смотрите в разделе Операторные функции.

Swift: 
3.0