Замыкания в 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 становится «замыканием», поскольку она ссылается на переменную, определённую вне функции. Процесс определения этой функции захватывает эту свободную переменную, образуя замкнутую функцию.

Полный пример

Пример

object Test {  
   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 изменяется, результат вычислений внутри замыкания изменяется соответствующим образом.

Пример

object ClosureExample1 {
  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.

Пример

object ClosureExample2 {
  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 и может изменять её.

Пример

object ClosureExample3 {
  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.

Пример

object ClosureExample4 {
  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
Другие расширения