Clojure中的范围规则

时间:2009-11-21 03:35:56

标签: 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

2 个答案:

答案 0 :(得分:23)

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

临时(线程本地)

binding更改了顶级Var x的值,这就是它所做的一切。如果let绑定到位,binding在决定要更改哪个值时不会看到它(并且let的绑定不是变量并且不可更改,因此'a好事还是它会给你一个错误)。并且binding不会屏蔽letbinding具有动态范围,因此它对顶级Vars的影响在binding表单和从binding表单内调用的任何函数中都可见。

访问普通旧x的值将为您提供绑定堆栈顶部的任何内容,最嵌套的let绑定值x(或函数)名为x的参数,或者如果您使用自己的宏或其他可能性,则替换为某个值x,并且默认情况下仅使用顶级Var x的当前值。没有其他约束力。

即使顶级Var xlet绑定x掩盖,您也可以通过@#'x访问顶级Var。试试这个版本,也许它会更有意义:

(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
---

答案 1 :(得分:14)

Clojure对符号使用词法范围 let,对于vars使用动态范围 binding 查看clojure的vars文档。

  • “进入功能”:到目前为止做得很好!符号x解析为var,这是抓取var x的“根绑定”。
  • “让”:本地绑定覆盖了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)