Подтвердить что ты не робот

Правила определения области видимости в Clojure

Несмотря на то, что я использовал Clojure, я не рассматривал подробные правила. Я больше смущен, когда читаю документацию. Я сделал небольшой тест, чтобы опробовать резолюции по определению области охвата и рассердился на сложность. Может ли кто-нибудь объяснить намерение и различные правила, которые использует Clojure?

(def x 1)

(defn dummy-fn2[]
    (+ x 1))        

(defn dummy-fn[]
    (println "entering function: " x)
      (let [x 100]
         (println "after let: " x)
         (let [x (dummy-fn2)]
            (println "after let and dummy2: " x)
            (binding [x 100]
             (println "after binding: " x)
             (let [x (dummy-fn2)]
               (println "after binding and dummy2: " x))))))

1:2 foo=> (dummy-fn)
entering function:  1
after let:  100
after let and dummy2:  2
after binding:  2
after binding and dummy2:  101
nil
4b9b3361

Ответ 1

let затеняет верхний уровень Var x локальным x. let не создает Var или не влияет на верхний уровень Var; он связывает некоторый символ таким образом, что локальные ссылки на этот символ будут заменены значением let -bound. let имеет лексическую область видимости, поэтому ее привязки видны только в самой форме let (не в функциях, вызываемых из let).

binding временно (thread-local) изменяет значение верхнего уровня Var x, что все, что он делает. Если привязка let на месте, binding не видит ее при выборе значения для изменения (и let привязки не являются vars и не изменяются, так что "хорошая вещь или это даст вам ошибка). И binding не будет маскировать let. binding имеет динамический масштаб, поэтому его влияние на верхние уровни Vars видны в форме binding и в любой функции, которая вызывается из формы binding.

Доступ к значению простого старого x даст вам все, что находится в верхней части стека привязок, либо самое вложенное let -граничное значение x (или функция paramater, называемая x, или некоторое значение x заменяется, если вы используете свой собственный макрос или другие возможности.) и по умолчанию использует только текущее значение верхнего уровня Var x, если нет другой привязки на месте.

Даже если верхний уровень Var x замаскирован символом let -bound x, вы всегда можете получить доступ к параметру Var с помощью @#'x. Попробуйте эту версию, возможно, это будет иметь смысл:

(def x 1)

(defn dummy-fn2[]
  (println "x from dummy-fn2:" x)
  (+ x 1))  

(defn dummy-fn[]
  (println "entering function:" x)
  (println "var x:" @#'x)
  (dummy-fn2)
  (println "---")
  (let [x 100]
    (println "after let:" x)
    (println "var x:" @#'x)
    (dummy-fn2)
    (println "---")
    (let [x (dummy-fn2)]
      (println "after let and dummy-fn2:" x)
      (println "var x:" @#'x)
      (dummy-fn2)
      (println "---")
      (binding [x 888]
        (println "after binding:" x)
        (println "var x:" @#'x)
        (dummy-fn2)
        (println "---")
        (let [x (dummy-fn2)]
          (println "after binding and dummy2:" x)
          (println "var x:" @#'x)
          (dummy-fn2)
          (println "---"))))))

дает:

entering function: 1
var x: 1
x from dummy-fn2: 1
---
after let: 100
var x: 1
x from dummy-fn2: 1
---
x from dummy-fn2: 1
after let and dummy-fn2: 2
var x: 1
x from dummy-fn2: 1
---
after binding: 2
var x: 888
x from dummy-fn2: 888
---
x from dummy-fn2: 888
after binding and dummy2: 889
var x: 888
x from dummy-fn2: 888
---

Ответ 2

Clojure использует как лексическую область let для символов, так и динамическую область binding для vars проверьте clojure vars документацию.

  • "ввод функции" : все в порядке! символ x разрешает var и это захватывает "корневую привязку" var x.
  • "после let" : локальная привязка закрыла var, символ x теперь равен 100, а не var.
  • "после let и dummy2" : x в dummy-fn2 относится к var x, поэтому он использует корневую привязку x и возвращает еще одну (+ 1 1)
  • "после привязки: сложный! привязка динамически заменила корневую привязку переменной var с именем x (которая была 1) с 100, но локальный символ x больше не является var, поэтому вы получаете локальную привязку.
  • "после привязки и dummy2" : привязка заменила корневое значение var x на 100, и это вернуло еще одно значение (+ 100 1)