Замыкания

Последнее обновление: 03.03.2024

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

Технически замыкание включает три компонента:

  • внешняя функция, которая определяет некоторую область видимости и в которой определены некоторые переменные и параметры - лексическое окружение

  • переменные и параметры (лексическое окружение), которые определены во внешней функции

  • вложенная функция, которая использует переменные и параметры внешней функции

fun outer(): ()->Unit{       // внешняя функция
    var n              // некоторая переменная - лексическое окружение
    fun inner(){     // вложенная функция
        // действия с переменной n
    }
	return ::inner  // возвращаем вложенную функцию
}

В данном случае функция inner и есть замыкание.

Рассмотрим замыкания на простейшем примере:

fun main() {
    val fn = outer() // fn = inner, так как функция outer возвращает функцию inner
    // вызываем внутреннюю функцию inner
    fn() // 6
    fn() // 7
    fn() // 8
}
fun outer(): ()->Unit{       // внешняя функция
    var n = 5         // некоторая переменная - лексическое окружение функции inner
    fun inner(){     // вложенная функция
        // действия с переменной n
        n++
        println(n)
    }
    return ::inner
}

Здесь функция outer задает область видимости, в которой определены внутренняя функция inner и переменная n. Переменная n представляет лексическое окружение для функции inner. В самой функции inner инкрементируем переменную n и выводим ее значение на консоль. В конце функция outer возвращает функцию inner.

Далее вызываем функцию outer:

val fn = outer()

Поскольку функция outer возвращает функцию inner, то переменная fn будет хранить ссылку на функцию inner. При этом эта функция запомнила свое окружение - то есть внешнюю переменную n.

Далее мы фактически три раза вызываем функцию inner, и мы видим, что переменная n, которая определена вне функции inner, увеличивается на единицу:

fn()   // 6
fn()   // 7
fn()   // 8

То есть несмотря на то, что переменная x определена вне функции inner, эта функция запомнила свое окружение и может его использовать, несомотря на то, что она вызывается вне функции outer, в которой была определена. В этом и суть замыканий.

Кстати мы можем сократить определение функции outer, используя анонимную функцию:

fun outer(): ()-> Unit{
    var n = 5
    return {
        n++
        println(n)
    }
}

Рассмотрим другой пример:

fun main() {
    val func = multiply(5)
    val result1 = func(6) // 30
    println(result1) 	   // 30

    val result2 = func(5) // 25
    println(result2)		  // 25
}
fun multiply(n:Int): (Int)->Int{

    return {m:Int-> n * m}
}

Итак, здесь вызов функции multiply() приводит к вызову другой внутренней функции. Внутренняя же функция представлена лямбда-выражением:

{m:Int-> n * m}

Оно запоминает окружение, в котором было создана, в частности, значение параметра n.

В итоге при вызове функции multiply определяется переменная func, которая и представляет собой замыкание, то есть объединяет две вещи: функцию и окружение, в котором функция была создана. Окружение состоит из любой локальной переменной или любого параметра, которые были определены в области действия функции multiply во время создания замыкания.

То есть result1 — это замыкание, которое содержит и внутреннюю функцию {m:Int-> n * m}, и параметр n, который существовал во время создания замыкания.

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

val func = multiply(5)

Число 5 передается для параметра n функции multiply.

При вызове внутренней функции:

val result1 = func(6) // 30

Число 6 передается для параметра m во внутреннюю функцию (int m) => n * m;

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

fun main() {
    val result1 = multiply(5)(6) // 30
    println(result1) 	   // 30

    val result2 = multiply(5)(5) // 25
    println(result2)		  // 25
}
fun multiply(n:Int): (Int)->Int{

    return {m:Int-> n * m}
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850