Замыкания в Scala

задняя часть переводчик Scala

В Scala функции обычно вводят входящие параметры, такие как(x: Int) => x > 0, только в теле функцииx > 0В качестве переменной используется x, единственный параметр этой функции.

В дополнение к этому, Scala также поддерживает ссылки на переменные, определенные в другом месте:(x: Int) => x + more, эта функция будетmoreОн же используется как вход, но откуда берется этот параметр? С точки зрения функции, more — это свободная переменная, потому что сам по себе функциональный литерал не имеет никакого смысла. Вместо этого x является переменной связывания, поскольку она четко определена в контексте функции: она определена как единственный параметр функции. Если вы используете только литерал этой функции, не определяя больше нигде в области видимости, компилятор будет жаловаться:

scala> (x: Int) => x + more
<console>:12: error: not found: value more
       (x: Int) => x + more

С другой стороны, тот же литерал функции работает нормально, пока можно найти переменную с именем more:

scala> var more = 1
more: Int = 1

scala> val addMore = (x: Int) => x + more
addMore: Int => Int = $$Lambda$1104/583744857@33e4b9c4

scala> addMore(10)
res0: Int = 11

Значение функции (объект), созданное во время выполнения из этого функционального литерала, называется замыканием. Название происходит от акта «захвата» его свободных переменных, тем самым «закрывая» функциональный литерал. Функциональные литералы без свободных переменных, например(x: Int) => x + 1, называемый закрытым языком (язык здесь относится к фрагменту исходного кода). Следовательно, значение функции, созданное из этого литерала функции во время выполнения, не является, строго говоря, замыканием, поскольку(x: Int) => x + 1Согласно нынешнему письму, он закрыт. и время выполнения из любого функционального литерала со свободными переменными, такими как(x: Int) => x + moreСозданная функция по определению обязана больше захватывать привязку своей свободной переменной. Соответствующий результат значения функции (содержащий ссылку на захваченную переменную more) называется замыканием, поскольку значение функции создается действием закрытия оператора open.

Этот пример поднимает вопрос: что произойдет, если после создания замыкания будет изменено больше? В Scala ответ заключается в том, что замыкание может видеть это изменение, см. следующий пример:

scala> more = 9999
more: Int = 9999

scala> addMore(10)
res1: Int = 10009

Интуитивно понятно, что замыкания Scala захватывают саму переменную, а не значение, на которое ссылается переменная. Как показано в предыдущем примере, для(x: Int) => x + moreСозданное замыкание может видеть изменения за пределами замыкания. Верно и обратное: изменения захваченных переменных замыканием также можно увидеть вне замыкания. Обратитесь к следующему примеру:

scala> val someNumbers = List(-11, -10, -5, 0, 5, 10)
someNumbers: List[Int] = List(-11, -10, -5, 0, 5, 10)

scala> var sum = 0
sum: Int = 0

scala> someNumbers.foreach(sum += _)

scala> sum
res3: Int = -11

В этом примере числа в списке суммируются путем обхода. Переменная sum находится в литерале функцииsum += _Охватывая область , эта функция добавляет число к сумме. Хотя среда выполнения является модификацией замыкания для суммы, окончательный результат -11 по-прежнему виден снаружи замыкания.

Так что, если замыкание обращается к переменной, которая делает несколько копий во время работы программы? Например, что, если замыкание использует локальные переменные функции, которая вызывается несколько раз? К какому экземпляру этой переменной замыкание обращается каждый раз?

Ответ таков: экземпляр, на который ссылается замыкание, — это тот, который был активен при создании замыкания. Ссылаясь на функцию ниже, функция создает и возвращает функцию большего закрытия

def makeIncreaser(more: Int) = (x: Int) => x + more

Каждый раз, когда вызывается функция, создается новое замыкание. Каждое замыкание обращается к переменной, которая была активной при его создании.

scala> val inc1 = makeIncreaser(1)
inc1: Int => Int = $$Lambda$1269/1504482477@1179731c

scala> val inc9999 = makeIncreaser(9999)
inc9999: Int => Int = $$Lambda$1269/1504482477@2dba6013

при звонкеmakeIncreaser(1), создается и возвращается замыкание, которое захватывает больше со значением привязки 1. Аналогично при вызовеmakeIncreaser(9999), возвращает замыкание, которое захватывает более связанное значение 9999. Когда вы применяете эти замыкания к входным параметрам, возвращаемый результат зависит от определения more при создании замыкания.

scala> inc1(10)
res4: Int = 11

scala> inc9999(10)
res5: Int = 10009

Здесь more является входным параметром вызова метода, и метод вернулся, но это не имеет никакого эффекта. Компилятор Scala реорганизует и упорядочит так, чтобы захваченные параметры находились в куче. Эта компоновка выполняется автоматически компилятором, и пользователю не нужно об этом заботиться.