Функции как значения
Чему вы научитесь:
- хранить функцию в переменной, передавать её аргументом и возвращать из другой функции;
- записывать замыкания коротко: без типов, с
$0и после скобок вызова; - обрабатывать массивы методами
map,filter,reduceиcompactMap; - объяснять, как замыкание захватывает переменную и почему две константы могут делить один счётчик.

Представьте карточку с рецептом. По ней можно приготовить блюдо прямо сейчас. А можно отдать карточку другу, убрать в коробку с рецептами или прикрепить к холодильнику. Готовить по ней будет тот, у кого она окажется, и тогда, когда ему понадобится.
В теме о функциях вы вызывали функцию сразу: писали имя, скобки и аргументы. Но с функцией можно обращаться и как с карточкой. Имя функции без скобок — сама карточка, а скобки после имени означают «приготовить сейчас».
Вот функция, которую положили в переменную:
func double(_ x: Int) -> Int {
x * 2
}
func square(_ x: Int) -> Int {
x * x
}
var transform: (Int) -> Int = double
print(transform(5)) // 10
transform = square
print(transform(5)) // 25Давайте разберём программу по шагам:
- Функции
doubleиsquareпринимаютIntи возвращаютInt. var transform: (Int) -> Int = doubleкладёт в переменную саму функциюdouble. Скобок послеdoubleнет, поэтому функция не вызывается.transform(5)вызывает то, что лежит в переменной, и печатает10.transform = squareкладёт в ту же переменную другую функцию.- Тот же вызов
transform(5)теперь печатает25.
Тип функции
У каждого значения в Swift есть тип, и у функции тоже. Тип функции записывают так: типы параметров в круглых скобках, стрелка и тип результата. Запись (Int) -> Int состоит из трёх частей:
(Int)— функция принимает одно целое число;->— стрелка, как в заголовке функции;Int— функция возвращает целое число.
Функции double и square делают разное, но тип у них один, поэтому обе помещаются в transform. Вот ещё три типа:
(Int, Int) -> Int— функция принимает два числа и возвращает число;(String) -> Void— функция принимает строку и ничего не возвращает;() -> Int— функция без параметров возвращает число.
Меток аргументов в типе нет. Если положить функцию add(a:b:) в константу op типа (Int, Int) -> Int, вызывают её без меток: op(2, 3). Вызов op(a: 2, b: 3) не скомпилируется.
В переменную помещается только функция подходящего типа. Функция, которая принимает строку, в transform не попадёт: компилятор сообщит, что типы не совпадают.
Функция как параметр
Раз функция — значение, её можно передать в другую функцию аргументом. Это как отдать карточку другу: он приготовит блюдо по вашему рецепту столько раз, сколько нужно.
func applyTwice(_ f: (Int) -> Int, to x: Int) -> Int {
f(f(x))
}
print(applyTwice(double, to: 3)) // 12
print(applyTwice(square, to: 3)) // 81Разберём первый вызов:
applyTwiceполучает функциюdoubleв параметрfи число3в параметрx.- Внутренний вызов
f(x)— этоdouble(3), он даёт6. - Внешний вызов
f(6)даёт12, и программа его печатает.
Во втором вызове в f лежит square: сначала 3 * 3, потом 9 * 9, то есть 81.
Функцию, которая принимает другую функцию или возвращает её, называют функцией высшего порядка. Работу здесь делят двое: applyTwice решает, сколько раз и с каким числом вызвать f, а переданная функция решает, что с этим числом сделать.
Функция как результат
Функцию можно и вернуть из другой функции. Тогда после стрелки в заголовке стоит тип функции:
func pick(big: Bool) -> (Int) -> Int {
if big {
return square
}
return double
}
let chosen = pick(big: true)
print(chosen(4)) // 16
print(pick(big: false)(4)) // 8Заголовок func pick(big: Bool) -> (Int) -> Int читают так: функция pick принимает Bool и возвращает функцию типа (Int) -> Int. Разберём вызовы:
pick(big: true)возвращает не число, а функциюsquare, и она попадает в константуchosen.chosen(4)вызываетsquareи печатает16.pick(big: false)(4)сначала получает функциюdouble, а вторые скобки тут же вызывают её с числом4. Получается8.
На собеседовании часто говорят: в Swift функции — значения первого класса. Это значит, что с функцией можно сделать то же, что с числом или строкой:
- положить в переменную или константу;
- передать аргументом в другую функцию;
- вернуть из функции.
Здесь картинка с карточкой перестаёт работать. Бумажную карточку отдают, и у вас её больше нет. А функцию можно положить сразу в десять переменных, и все они ведут к одной и той же функции.
Частая ошибка
Функцию передали со скобками:
print(applyTwice(double(3), to: 3)) // ✗error: cannot convert value of type 'Int' to expected argument type '(Int) -> Int'Программа не компилируется. Скобки после double вызывают функцию прямо на месте, и double(3) даёт число 6. А параметр f ждёт функцию типа (Int) -> Int, а не Int. Это всё равно что отдать другу не карточку, а уже готовое блюдо. Передавайте имя без скобок: applyTwice(double, to: 3).
Главное
- Функция — значение: её кладут в переменную, передают аргументом и возвращают из другой функции.
- Тип функции записывают как
(Int) -> Int: типы параметров в скобках, стрелка и тип результата; меток аргументов в типе нет. - Функция высшего порядка принимает другую функцию или возвращает её.
- Имя без скобок — сама функция, а имя со скобками — её вызов и результат.