2009-11-21 18 views
16

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 
+3

を、おそらくあなたは、あなたが期待するかを説明でき、なぜそれが実際に何が起こるかは異なるのです。 –

答えて

14

ClojureのはVARSためレキシカルスコープのシンボルのためのlet動的スコープbindingの両方を使用してのClojureのvarsドキュメントをチェックアウト 。

  • "入力機能":罰金、これまでにやって!シンボルxはvarに解決され、これはvar xの "root binding"を取得しています。
  • "後で":ローカルバインディングがvarを覆っていたので、シンボルxは現在varではありません。 "LETとdummy2後"
  • :ダミー-FN2におけるxがVARのXを指し、それはXの結合ルートを使用し、それよりも1を返した(1 + 1)
  • 「結合後":トリッキーなひと!バインディングは動的にxという名前のvarのルートバインドを100に置き換えましたが、ローカルシンボルxはvarではなくなり、ローカルバインディングを取得します。「結合とdummy2後」
  • :結合は、100とVAR xのルート値を置換し、これはそれ以上一方(+ 100 1)が返さと
+0

バインドのように見えますが、 "シンボルxが解決するvar"ではなく、 "varと名前x"が置き換えられますか? –

+0

"varと名前x"は何を意味しますか? –

+0

いずれかのvarが(resolve 'x)によって返されます。 –

22

let影トップレベルVarのxをローカルxletは、Varを作成したり、トップレベルのVarに影響を与えません。そのシンボルへのローカル参照がletの値に置き換えられるように、いくつかのシンボルをバインドします。 letはレキシカルスコープを持っているため、そのバインディングはletフォーム内でのみ表示されます(letから呼び出される関数ではありません)。

binding一時的に(スレッドローカル)、最上位のVar xの値が変更されます。それだけです。 letバインディングがある場合、bindingは、変更する値を決定するときに表示されません(letのバインディングは変数ではなく、変更できないため、エラーが発生します)。 bindingletをマスクしません。 bindingはダイナミックスコープを持っているため、bindingフォーム内およびbindingフォーム内から呼び出される関数内で、最上位変数に及ぼすその影響が表示されます。

は、バインディングのスタックの一番上にあるものは何でもあなたを与えるだろう、昔ながらのxの値へのアクセスのいずれかxの最もネストされたlet結合型の値(または関数のparamaterはxと呼ばれる、またはいくつかの値xが置き換えられあなた自身のマクロを使用する場合、または他の可能性を使用している場合)、他のバインディングが存在しない場合には、デフォルトで最上位変数Var xの現在の値のみを使用します。

トップレベルヴァールxlet結合型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 "---")))))) 

は与える:ドキュメントの繰り返しを避けるために

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 
--- 
+0

私はこのコードを実行すると 'IllegalStateException動的に非動的varをバインドできません 'というメッセージが表示されます。これは、 'binding'フォームを' let'に置き換えるか、 '(def ^:dynamic x1)'を使って解決します。 – dkinzer