2017-02-05 2 views
0

Я смотрел на источник для memoize. Исходя из таких языков, как C++/Python, эта часть ударила меня трудно: (let [mem (atom {})] (fn [& args] (if-let [e (find @mem args)] ...Когда clojure удаляет переменную?

Я понимаю, что memoize возвращает функцию, но и для хранения состояния, он использует локальный «переменный» mem. Но послеmemoize возвращает функцию, не следует, чтобы внешняя версия исчезла из области видимости. Как функция может по-прежнему ссылаться на mem.

Почему Clojure не удаляет эту внешнюю переменную и как она управляет именами переменных. Например, я делаю еще одну memoized функцию, тогда memoize использует другой mem. Разве это имя не столкнулось с более ранним mem?

P.S .: Я думал, что там должно быть что-то много, происходит там, что мешает, так что я написал себе более легкий вариант, который идет как http://ideone.com/VZLsJp, но до сих пор работает как memoize.

+0

@Flimzy, спасибо за оценку: D –

ответ

5

Объекты являются сборщиками мусора, если ни один поток не может получить к ним доступ, как обычно для языков JVM. Если поток имеет ссылку на функцию, возвращаемую memoize, и функция имеет ссылку на атом в mem, тогда транзитивно атом все еще доступен.

Но после того, как memoize возвращает функцию, не следует, чтобы внешняя версия исчезала из области видимости. Как функция может по-прежнему ссылаться на mem.

Это то, что называется closure. Если функция определена с использованием имени из своей среды, она впоследствии ссылается на это значение - даже если среда определения отсутствует, а функция - это единственное, что имеет доступ.

Как и я, я делаю еще одну memoized функцию, затем memoize использует другую mem. Разве это имя не сталкивается с более ранним mem?

Нет, за исключением, возможно, путаных программистов. Наличие нескольких областей, каждая из которых объявляет свое собственное имя mem, очень возможно, и обычные правила lexical scoping используются для определения того, что подразумевается при чтении mem. Есть несколько сложнее крайние случаи, такие как

(let[foo 2] 
    (let[foo (fn[] foo)] ;; In the function definition, foo has the value from the outer scope 
    ;; because the second let has not yet bound the name 
    (foo))) ;; => 2. 

, но в целом идея довольно проста - значение имени является одним дано в определении ближайшего в тексте программы на месте она используется - либо в локальный охват или в ближайшей внешней области. Различные вызовы memoize создают различные замыкания, так что имя mem относится к разным атомам в каждой возвращаемой функции.

+1

Спасибо! Узнал что-то новое: Закрытие в Clojure. :) –

+1

И на всякий случай, [Python имеет закрытие тоже] (http://stackoverflow.com/a/4020443/4534687)! –

Смежные вопросы