Clojure中的范围规则

unj*_*nj2 18 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
Run Code Online (Sandbox Code Playgroud)

Bri*_*per 24

letx使用本地阴影调制toplevel Var x. let不会创建Var或影响toplevel Var; 它绑定一些符号,以便对该符号的本地引用将替换为let-bound值. let具有词法范围,因此其绑定仅在let表单本身内可见(而不是在其中调用的函数中let).

binding临时(线程本地)更改顶级Var的值x,这就是它所做的全部.如果let绑定到位,binding在决定要更改哪个值时不会看到它(并且let绑定不是变量并且不可更改,因此'好事或者它会给您一个错误).并且binding不会掩盖let. binding具有动态范围,因此它对于顶层Vars的影响在binding表单和从binding表单内调用的任何函数中都是可见的.

访问plain old的值x将为您提供绑定堆栈顶部的任何内容,或者是最嵌套的绑定letx(或调用的函数参数x,或者x如果您使用自己的宏,则替换某些值,或其他可能性.),并且x如果没有其他绑定,则默认情况下仅使用顶层Var的当前值.

即使顶层瓦尔x由掩盖let结合的x,你可以随时访问通过顶层无功@#'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 "---"))))))
Run Code Online (Sandbox Code Playgroud)

得到:

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
---
Run Code Online (Sandbox Code Playgroud)


Art*_*ldt 14

Clojure使用符号的词法范围 let和vars的动态范围 binding检查clojure的vars文档.

  • "进入功能":到目前为止做得很好!符号x解析为var,这是抓取var x的"根绑定".
  • "after let":局部绑定覆盖了var,符号x现在是100而不是var.
  • "在let和dummy2之后":dummy-fn2中的x指的是var x,所以它使用x的根绑定并返回一个多于那个(+ 1 1)
  • "绑定后":棘手的一个!绑定动态地将名为x(其为1)的var的根绑定替换为100,但是本地符号x不再是var,因此您获得本地绑定.
  • "在绑定和dummy2之后":绑定将var x的根值替换为100,并且返回的值超过了(+ 100 1)