Перейти к содержанию
Educora
Средний18 мин6 / 14

Функции и замыкания

Разбивай код на функции: метки аргументов, значения по умолчанию и возвращаемые значения. Затем изучи замыкания, их краткие формы и замыкания в конце вызова.

Проверь себя
В этом уроке ты узнаешь
  • Писать и вызывать функции с параметрами и возвращаемыми значениями
  • Использовать метки аргументов, _ и значения параметров по умолчанию
  • Писать замыкания, сокращать их и узнавать синтаксис замыкания в конце вызова

Представь, что в приложении цена билета считается в десяти разных местах. Когда правила изменятся, придётся исправлять десять мест — и одно ты наверняка забудешь. Решение — функция: ты пишешь код один раз, даёшь ему имя и вызываешь там, где нужно. Ты уже пользовался готовыми функциями print, sorted(), map — теперь напишешь свои.

Объявление функции

Функция начинается с ключевого слова func. В скобках записываются параметры вместе с типами, а после -> — тип возвращаемого значения. return возвращает результат. Если тело функции состоит из одного выражения, слово return можно не писать.

Swift
func greet(name: String) -> String {
    return "Hello, \(name)!"
}

func square(_ x: Int) -> Int {
    x * x
}

print(greet(name: "Aysel"))
print(square(7))
print(square(square(2)))
Ожидаемый результат
Hello, Aysel!
49
16

Метки аргументов и значения по умолчанию

В Swift у каждого параметра может быть два имени, чтобы вызов читался как предложение: метка аргумента (пишется при вызове) и имя параметра (используется внутри функции). Функцию travel(from start: String, to end: String) вызывают так: travel(from: "Baku", to: "Shaki"). Если метка не нужна, вместо неё пишут _ — именно так устроен square(7).

Параметру можно задать значение по умолчанию: если опустить его при вызове, будет взято это значение. Ниже билет стоит 5 манатов для детей до 12 лет, 7,5 маната для студентов и 10 манатов для остальных.

Swift
func travel(from start: String, to end: String) {
    print("Travelling from \(start) to \(end)")
}

func ticketPrice(for age: Int, isStudent: Bool = false) -> Double {
    if age < 12 {
        return 5.0
    } else if isStudent {
        return 7.5
    }
    return 10.0
}

travel(from: "Baku", to: "Shaki")
print(ticketPrice(for: 8))
print(ticketPrice(for: 20))
print(ticketPrice(for: 20, isStudent: true))
Ожидаемый результат
Travelling from Baku to Shaki
5.0
10.0
7.5

Замыкания

Определение
Замыкание (closure)

Блок кода без имени, который можно сохранить в переменную и передать в другую функцию. Записывается в фигурных скобках: сначала параметры и возвращаемый тип, затем слово in, а за ним тело.

Swift позволяет сокращать замыкания шаг за шагом: он выводит типы из контекста, не требует return для одного выражения и разрешает обращаться к параметрам как $0, $1. Ниже одна и та же сортировка записана четыре раза, всё короче и короче:

Swift
let double = { (x: Int) -> Int in
    return x * 2
}
print(double(5))

let numbers = [5, 2, 9, 1]
print(numbers.sorted(by: { (a: Int, b: Int) -> Bool in
    return a > b
}))
print(numbers.sorted(by: { a, b in a > b }))
print(numbers.sorted { $0 > $1 })
print(numbers.sorted(by: >))
Ожидаемый результат
10
[9, 5, 2, 1]
[9, 5, 2, 1]
[9, 5, 2, 1]
[9, 5, 2, 1]

Замыкания в конце вызова

Если замыкание — последний аргумент функции, его можно написать после скобок — это называется замыканием в конце вызова (trailing closure). Именно так записаны numbers.sorted { $0 > $1 } и prices.map { $0 * 2 }. Можно написать и свою функцию, принимающую замыкание; тип её параметра — функциональный, например () -> Void.

Swift
func repeatTask(times: Int, task: () -> Void) {
    for _ in 1...times {
        task()
    }
}

repeatTask(times: 3) {
    print("Hip hip hooray!")
}

func apply(_ value: Int, _ operation: (Int) -> Int) -> Int {
    operation(value)
}

func triple(_ x: Int) -> Int {
    x * 3
}

print(apply(4) { $0 + 10 })
print(apply(4, triple))
Ожидаемый результат
Hip hip hooray!
Hip hip hooray!
Hip hip hooray!
14
12
В функцию можно передать как замыкание, так и другую именованную функцию.

Замыкания запоминают переменные из того места, где они были созданы (по-английски capture — «захват»). makeCounter ниже возвращает счётчик, который при каждом вызове увеличивается на единицу: переменная count продолжает жить внутри замыкания даже после завершения функции.

Swift
func makeCounter() -> () -> Int {
    var count = 0
    return {
        count += 1
        return count
    }
}

let counter = makeCounter()
print(counter())
print(counter())
print(counter())
Ожидаемый результат
1
2
3

Главное

  • Функция объявляется так: func name(parameter: Type) -> ReturnType { ... }.
  • Метка аргумента пишется при вызове, имя параметра — внутри функции; _ убирает метку.
  • Параметр со значением по умолчанию можно не указывать при вызове.
  • Замыкание — функция без имени; $0, $1 — её параметры.
  • Замыкание — последний аргумент — можно писать после скобок; замыкания запоминают окружающие переменные.

Проверь себя

Вопросов: 10. Каждый правильный ответ приносит XP.

1 / 10
Какой вызов функции func travel(from start: String, to end: String) правильный?