Больше объяснения на Лексическом Закреплении в Закрытиях?

Есть, многие ТАК отправляют связанный с этим, но я спрашиваю это снова с другой целью

Я пытаюсь понять, почему закрытия важны и полезны. Одна из вещей, которые я прочитал в другом ТАК посты, связанные с этим, - то, что, когда Вы передаете переменную, чтобы закрыть прения, закрытие начинает помнить эту стоимость с того времени вперед. Это весь Технический аспект его или есть больше к тому, что происходит там.

Что интересно, тогда то, что произошло бы, когда переменная, используемая в закрытии, изменена снаружи. Они должны быть константами только?

На языке Clojure я могу сделать следующее: Но с тех пор есть стоимость, неизменно, эта проблема не возникает. Что относительно других языков и каково надлежащее техническое определение закрытия?

(defn make-greeter [greeting-prefix]
    (fn [username] (str greeting-prefix ", " username)))

((make-greeter "Hello") "World")
8
задан Baishampayan Ghose 16 December 2009 в 05:46
поделиться

8 ответов

Замыкание - это действительно структура данных, используемая компилятором, чтобы гарантировать, что функция всегда будет иметь доступ к данным, которые ей необходимы для работы. вот пример функции, которая записывает, когда она была определена.

(defn outer []
    (let [foo (get-time-of-day)]
      (defn inner []
          #(str "then:" foo " now:" (get-time-of-day)))))


(def then-and-now (outer))
(then-and-now)    ==> "then:1:02:03 now:2:30:01"
....
(then-and-now)    ==> "then:1:02:03 now:2:31:02"

когда эта функция определена, создается класс и небольшая структура (закрытие) выделяется в куче, в которой хранится значение foo. у класса есть указатель на это (или он содержит его, я не уверен). если вы запустите это снова, тогда будет выделено второе закрытие для хранения этого другого foo. Когда мы говорим «эта функция закрывается по foo», мы имеем в виду, что у нее есть ссылка на структуру / класс / все, что хранит состояние foo на момент его компиляции. Причина, по которой вам нужно закрыть что-то, заключается в том, что функция, которая его содержит, уходит до того, как данные будут использованы. В этом случае external (который содержит значение foo) будет заканчиваться и исчезнуть задолго до того, как foo будет использован, поэтому никого не будет рядом, чтобы изменить foo. конечно, foo может передать ссылку кому-нибудь, кто затем может ее изменить.

5
ответ дан 5 December 2019 в 06:09
поделиться

лексическое замыкание - это то, в котором заключенные переменные (например, префикс приветствия в вашем примере) заключены по ссылке. Созданное закрытие не просто получает значение префикса приветствия во время его создания, но и получает ссылку. Если префикс приветствия изменен после создания замыкания, то его новое значение будет использоваться замыканием каждый раз, когда оно вызывается.

В чистых функциональных языках это не большая разница, потому что ценности никогда не меняются. Таким образом, не имеет значения, копируется ли значение welcome-prefix в закрытие: нет никакой разницы в поведении, которая могла бы возникнуть из-за ссылки на оригинал по сравнению с его копией.

В "императивном-" языки с закрытием ", таких как C # и Java (через анонимные классы), необходимо принять решение о том, заключена ли заключенная переменная по значению или по ссылке. В Java это решение отменяется тем, что разрешается включать только переменные final , эффективно имитируя функциональный язык в том, что касается этой переменной. В C # я считаю, что это другое дело.

Заключение по значению упрощает реализацию: переменная, которая должна быть заключена, часто существует в стеке и, следовательно, будет уничтожена, когда функция, создающая замыкание, вернется - это означает, что она может ' Прилагается ссылкой. Если вам нужно вложение по ссылке, обходной путь состоит в том, чтобы идентифицировать такие переменные и сохранять их в объекте, выделяемом каждый раз при вызове этой функции. Затем этот объект сохраняется как часть закрытия ' s и должны оставаться в рабочем состоянии до тех пор, пока живы все закрытия, использующие его. (Я не знаю, используют ли эти скомпилированные языки напрямую эту технику.)

5
ответ дан 5 December 2019 в 06:09
поделиться

Замыкание можно рассматривать как «среду», в которой имена привязаны к значениям . Эти имена полностью принадлежат закрытию, поэтому мы говорим, что оно «закрывает» свое окружение. Итак, ваш вопрос не имеет смысла в том смысле, что «внешнее» не может влиять на закрытую среду. Да, замыкание может относиться к имени в глобальной среде (другими словами, если оно использует имя, не привязанное к его частной, закрытой среде), но это другая история.

Если хотите, вы можете думать о среде как о словаре или хэш-таблице. Замыкание получает свой собственный небольшой словарь, в котором ищутся имена.

2
ответ дан 5 December 2019 в 06:09
поделиться

Возможно, вам понравится прочитать О лямбдах, захвате и изменяемости , в котором для сравнения описано, как это работает в C # и F #.

2
ответ дан 5 December 2019 в 06:09
поделиться

Have a look at this blog post: ADTs in Clojure. It shows a nice application of closures to the problem of locking up data so that it is accessible exclusively through a particular interface (rendering the data type opaque).

The main idea behind this type of locking is more simply illustrated with the counter example, which huaiyuan posted in Common Lisp while I was composing this answer. Actually, the Clojure version is interesting in that it shows that the issue of a closed-over variable changing its value does arise in Clojure if the variable happens to hold an instance of one of the reference types.

(defn create-counter []
  (let [counter (atom 0)
        inc-counter! #(swap! counter inc)
        get-counter (fn [] @counter)]
    [inc-counter! get-counter]))

As for the original make-greeter example, you could rewrite it thus (note the deref/@):

(defn make-greeter [greeting-prefix]
    (fn [username] (str @greeting-prefix ", " username)))

Then you can use it to render personalised greetings from the different operators of various sections of a website. :-)

((make-greeter "Hello from Gizmos Dept") "John")
((make-greeter "Hello from Gadgets Dept") "Jack").
1
ответ дан 5 December 2019 в 06:09
поделиться

Это не тот ответ, который появляется чтобы получить здесь голоса, но я от всей души призываю вас найти ответ на свой вопрос, прочитав (бесплатно!) (онлайн!) учебник Шрирама Кришнамурти, Языки программирования: применение и интерпретация .

] Я перефразирую книгу очень, очень кратко, резюмируя развитие крошечных интерпретаторов, через которые она ведет вас:

  • язык арифметических выражений (AE)
  • язык арифметических выражений с именованные выражения (WAE); реализация этого требует разработки функции замены , которая может заменять имена значениями
  • язык, который добавляет функции первого порядка (F1WAE): использование функции включает замену значения для каждого из имен параметров.
  • Тот же язык, без подстановки: оказывается, что «среды» позволяют избежать накладных расходов на упреждающую подстановку.
  • язык, который устраняет разделение между функциями и выражениями. позволяя функции, которые должны быть определены в произвольных местах (FWAE)

Это ключевой момент: вы реализуете это, а затем обнаруживаете, что при подстановке он работает нормально, но в средах он не работает. В частности, чтобы исправить это, вы должны обязательно связать с определением оцениваемой функции среду, которая была на месте, когда она оценивалась. Эта пара (fundef + environment-of-definition) и есть то, что называется «закрытием».

Уф!

Хорошо, что происходит, когда мы добавляем изменяемые привязки к изображению? Если вы попробуете это сами, то увидите, что естественная реализация заменяет среду, которая связывает имена со значениями, на среду, которая связывает имена с привязками. Это ортогонально понятию замыканий; поскольку замыкания захватывают среды, и поскольку среды теперь сопоставляют имена привязкам,

9
ответ дан 5 December 2019 в 06:09
поделиться

Вы можете думать о закрытии как о "среду", в которой имена связаны со значениями. Эти имена являются полностью приватны для замыкания, поэтому вот почему мы говорим, что она "закрывает" свое окружение. Поэтому ваш вопрос не имеет смысла, так как "внешнее" не может повлиять на закрытую среду. Да, закрытие может ссылаться на имя в глобальном окружении (другими словами, если оно использует имя, которое не связано в своей частной, закрытой среде), но это уже другая история.

Я полагаю, что вопрос заключался в том, возможны ли подобные вещи в языках, допускающих мутацию локальных переменных:

CL-USER> (let ((x (list 1 2 3)))
           (prog1
               (let ((y x))
                 (lambda () y))
             (rplaca x 2)))
#<COMPILED-LEXICAL-CLOSURE #x9FEC77E>
CL-USER> (funcall *)
(2 2 3)

И - поскольку они, очевидно, возможны - я думаю, что вопрос правомерен.

0
ответ дан 5 December 2019 в 06:09
поделиться