Замыкания в Scala
В Scala замыкание — это функция, которая может захватывать и включать свободные переменные из своего определяющего окружения.
Замыкание — это функция, возвращаемое значение которой зависит от одной или нескольких переменных, объявленных вне функции.
Замыкание может получать доступ к переменным вне функции и использовать их внутри функции. Даже когда область видимости переменных выходит за пределы своего определяющего окружения, замыкание всё ещё может получать доступ к этим переменным.
Ключевая особенность замыкания в том, что оно может «замыкаться над» переменными вне функции, делая эти переменные по-прежнему доступными внутри функции.
Как следующая анонимная функция:
val multiplier = (i:Int) => i * 10
Внутри тела функции есть переменная i, которая является параметром функции. Как в следующем фрагменте кода:
val multiplier = (i:Int) => i * factor
В multiplier есть две переменные: i и factor. Одна из них, i, является формальным параметром функции; при вызове функции multiplier переменной i присваивается новое значение. Однако factor — не формальный параметр, а свободная переменная. Рассмотрим следующий код:
var factor = 3 val multiplier = (i:Int) => i * factor
Здесь мы вводим свободную переменную factor, которая определена вне функции.
Определённая таким образом переменная-функция multiplier становится «замыканием», поскольку она ссылается на переменную, определённую вне функции. Процесс определения этой функции захватывает эту свободную переменную, образуя замкнутую функцию.
Полный пример
Пример
def main(args: Array[String]) {
println( "muliplier(1) value = " + multiplier(1) )
println( "muliplier(2) value = " + multiplier(2) )
}
var factor = 3
val multiplier = (i:Int) => i * factor
}
Выполнение приведенного выше кода дает следующий вывод:
$ scalac Test.scala $ scala Test muliplier(1) value = 3 muliplier(2) value = 6
В следующем примере функция-замыкание захватывает внешнюю переменную externalVar. Когда значение externalVar изменяется, результат вычислений внутри замыкания изменяется соответствующим образом.
Пример
def main(args: Array[String]): Unit = {
var externalVar = 10
val closure = (x: Int) => x + externalVar
println(closure(5)) // Вывод 15
externalVar = 20
println(closure(5)) // Вывод 25
}
}
Выполнение приведенного выше кода дает следующий вывод:
15 25
Замыкания как возвращаемые значения
В следующем примере функция makeAdder возвращает замыкание. Различные экземпляры замыкания захватывают разные значения adder.
Пример
def main(args: Array[String]): Unit = {
def makeAdder(adder: Int): Int => Int = {
(x: Int) => x + adder
}
val addFive = makeAdder(5)
val addTen = makeAdder(10)
println(addFive(3)) // Вывод 8
println(addTen(3)) // Вывод 13
}
}
Выполнение приведенного выше кода дает следующий вывод:
8 13
Типы переменных, захватываемых замыканиями
Замыкания могут захватывать переменные разных типов, включая типы значений и ссылочные типы.
В следующем примере замыкание addMessage захватывает переменную messages и может изменять её.
Пример
def main(args: Array[String]): Unit = {
var messages = List("Hello", "World")
val addMessage = (msg: String) => messages = messages :+ msg
addMessage("Scala")
println(messages) // Вывод List(Hello, World, Scala)
}
}
Выполнение приведенного выше кода дает следующий вывод:
List(Hello, World, Scala)
Замыкания и функциональное программирование
Замыкания — важная концепция в функциональном программировании, и они широко используются в таких техниках, как функции высшего порядка и каррирование.
В следующем примере функция highOrderFunction принимает функцию в качестве параметра. Переданное замыкание захватывает внешнюю переменную externalVar.
Пример
def main(args: Array[String]): Unit = {
def highOrderFunction(f: Int => Int, x: Int): Int = f(x)
val externalVar = 5
val closure = (x: Int) => x + externalVar
println(highOrderFunction(closure, 10)) // Вывод 15
}
}
Выполнение приведенного выше кода дает следующий вывод:
15Другие расширения