28

動的変数とバインディング関数を理解しようとしたので、これを試しました(clojure 1.3):

user=> (defn f [] 
           (def ^:dynamic x 5) 
           (defn g [] (println x)) 
           (defn h [] (binding [x 3] (g))) 
           (h))
#'user/f
user=> (f)     
5
nil

混乱して、私はこのやや単純なコードを試しました:

user=> (def ^:dynamic y 5)
#'user/y
user=> (defn g [] (println y))
#'user/g
user=> (defn h [] (binding [y 3] (g)))
#'user/h
user=> (h)
3
nil

2つのコードの違いは何ですか?2番目の例は機能するのに、最初の例は機能しないのはなぜですか?

ヒント:次の作業に気づきました(理由はまだ完全には理解されていません)。

user=> (def ^:dynamic y 5)
#'user/y
user=> (defn f [] (defn g [] (println y)) (defn h [] (binding [y 3] (g))) (h))
#'user/f
user=> (f)
3
nil
user=> 
4

1 に答える 1

37

Clojure 1.4で最初の例を実行すると、結果として3が得られます(予想どおり)。新しいREPLでこれを試しましたか?

^:dynamicdefシンボル(で定義されている)が動的に(で定義されている)リバウンドされることを意図しているというClojureコンパイラーへの命令bindingです。

例:

(def foo 1)
(binding [foo 2] foo)
=> IllegalStateException Can't dynamically bind non-dynamic var: ...

(def ^:dynamic bar 10)
(binding [bar 20] bar)    ;; dynamically bind bar within the scope of the binding
=> 20
bar                       ;; check underlying value of bar (outside the binding)
=> 10

binding呼び出し元のスレッド内に動的スコープがあることに注意してください。バインディング内で呼び出された関数には、変更された値bar(20)が表示されますが、他のスレッドには変更されていないルート値の10が表示されます。

最後に、役立つと思われるいくつかのスタイルポイント:

  • 囲んでいる名前空間に影響を与えるため、関数内defに配置することは一般的に悪い考えと考えられています。defn関数内では、代わりに使用する必要があります(let [foo bar] ...)
  • 使用したいbinding場合は、通常、代わりに高階関数を使用して同じ結果を達成できるかどうかを検討する必要があります。binding一部のコンテキストでは便利ですが、一般にパラメーターを渡すのに適した方法ではありません。通常、関数の合成は長期的には優れています。これはbinding、関数の実行に必要な暗黙のコンテキストが作成され、テスト/デバッグが困難になる可能性があるためです。
于 2012-07-31T02:12:58.043 に答える