#scala #functional-programming #closures
#scala #функциональное программирование #закрытие
Вопрос:
В следующей функции, называемой inc в Scala, которая выполняет операцию приращения.
def inc(more:Int) = { def helper(x:Int) = x more helper _ }
Всякий раз, когда вызывается функция inc, она возвращает другую функцию, которая связывает переданный ей аргумент. Например, inc(1) вернет другую функцию типа Int =gt; Int, где переменная more связана с 1.
inc(1) // This is of type Int =gt; Int
Итак, можем ли мы сказать, что more является переменной состояния возвращаемой функции, и когда мы вызываем inc(1), то 1 присваивается more?
Вот еще несколько уточнений,
Поскольку я исхожу из парадигмы программирования OO, когда я говорю «состояние», я соотносил его с экземпляром класса, который в данный момент времени имеет определенное состояние. Давайте сначала рассмотрим класс в Java call IncHelper следующим образом:
class IncHelper{ private int more; public IncHelper(int more){ this.more = more; } public int inc(int x){ return x this.more; } }
Если я создам другой экземпляр вышеуказанного класса следующим образом:
IncHelper inc1 = new IncHelper(1); // This instance will always increase a value by 1 inc1.inc(10); // output will be 11
Если я создам другой экземпляр вышеуказанного класса следующим образом:
IncHelper inc2 = new IncHelper(2); // This instance will always increase a value by 2 inc2.inc(10); // output will be 12
Таким образом, в приведенных выше двух сценариях два экземпляра inc1 и inc2 содержат два разных значения переменных состояния. И то же самое верно для примера, который я привел для функционального программирования Scala:
val inc1 = inc(1) inc1(10) // Will return 11
If I create another value as follows:
val inc2 = inc(2) inc2(10) // Will return 12
So in both cases i.e. OO Programming when I created 2 instances of IncHelper, it remembers the variable which is passed while constructing it. Same way, it is true for the two function literals we have created, where the two variables passed while creating the function literals inc1 and inc2 has stored the values.
Ответ №1:
Закрытие функции хранит информацию о контексте — другой вопрос, состоит ли этот контекст из изменяемых и/или неизменяемых данных.
Мы бы назвали функцию с сохранением состояния только в том случае, если в ней было какое-то наблюдаемое изменяемое состояние (что делает функцию непрозрачной для ссылок; побочные эффекты, такие как печать, считаются изменяемым состоянием). Таким образом, если у вас нет каких-либо данных, которые можно было бы мутировать (наличие var
там данных, которые никогда не мутировали, делает их эффективными неизменяемыми), ваша функция не имеет состояния в соответствии с обычной терминологией.
В вашем случае:
def inc(more:Int) = { def helper(x:Int) = x more helper _ }
inc
не хранит изменяемые данные. x more
возвращает новое значение, не изменяя ни x
значения, ни more
значения. Если вы это сделаете:
inc(1) inc(1) inc(1) inc(1)
вы будете получать один и тот же результат ( Int =gt; Int
функцию с одинаковым поведением) каждый раз без побочных эффектов. Таким образом, согласно общепринятой терминологии, inc
функция не имеет состояния, и вы не можете сказать, что у нее есть состояние. Тот факт, что в нем хранятся некоторые данные… не имеет значения, потому что каждый фрагмент кода, который вы когда-либо писали, делает это, поэтому все будет называться с сохранением состояния, и различие между состоянием и состоянием без состояния будет практически бесполезным.
Теперь, если вы определили, что вы функционируете как:
var state = 0 def inc(more:Int) = { state = state more val y = state // binds current value of state def helper(x:Int) = x y helper _ }
каждый вызов inc
с одним и тем же значением приведет к другому значению (здесь: функция Int =gt; Int
с другим поведением), так что будет о каком-то состоянии говорить. ( int
функция будет иметь сохраненное состояние , в var state
то время как возвращаемые функции будут без состояния, поскольку они будут захватывать только неизменяемые значения).
Это состояние было бы независимым от концепции закрытия — закрытие только «говорит», что определение функции использует некоторые данные, предоставленные в момент ее создания, И что функция все еще может получить доступ к этим данным после того, как вы больше не сможете получить доступ извне.
Подводя итог, я бы сказал только, что (конкретное) «закрытие функции сохраняет состояние функции», если в среде было какое-то изменяемое состояние, в котором была создана функция, которая была захвачена (использована) этой функцией. В других случаях я бы этого не сказал, так как не было бы никакого «состояния» для захвата. Однако, поскольку это довольно неточное утверждение (делает ли функция снимок изменяемого состояния, которое само по себе не изменяемо? или захваченная функция ссылается на некоторые изменяемые данные, которые могут быть изменены вне ее?) Я бы вообще этого избегал.
Комментарии:
1. Спасибо Матеушу Кубушоку, так как я больше ориентируюсь на парадигму УПС, следовательно, я соотносил ее с УПС. Я подробно изложил свой вопрос, в котором я попытался сопоставить эти два.
2. «Экземпляр», «объект», «состояние» и т. Д.-Это детали реализации, которые вы можете наблюдать в JVM (что является OO по дизайну). Но те же понятия («функция», «закрытие») может быть реализована по-разному, например, в Haskell вы бы замыканий реализуется с некоторыми низкоуровневыми c-концепции (невидима для программиста), а в C закрытие захвата вполне явной (эту странную
[]
штуковину) а лямбда может быть встроен в сырых АСМ инструкции без выделения памяти. Восприятие «закрытия» как «состояния» может стать способом полного использования этой концепции.