2012-10-08 13 views
8

私は以下のことが多くの理由から悪い考えであることを認識しています。私は23歳のスタックオーバーフロー担当者がいることを考えれば、私はプログラムを学ぶ新しい学習者であると仮定するのは自然です。しかし、私をユーモアにして、「どうしたいのですか/これをしたくないのですか」というのではなく、「どうすればこのことをすることができますか」に焦点を当ててください。私が欲しいものClojure Dynamic Binding

(def dog (Dog. ...)) 
(def cat (Cat. ...)) 

(with-animal dog 
    (println (str "Dog: " (speak) "\n"))) 
(with-animal cat 
    (println (str "Cat: " (speak) "\n"))) 

出力する:

Dog: woof 
Cat: meow 

だから基本的に、私はと動物はマクロS。T.になりたいです"speak"関数呼び出しのすべての発生は、私がブロックを呼び出しているオブジェクトにマップされます。

特に、私は書きたくない:

(let-binding [speak (fn [] "woof")] ...) 
(let-binding [speak (fn [] "meow")] ...) 

はむしろ、私が欲しいは私が呼んでいるオブジェクトのいくつかの方法に関数マップを話すと、動物のようにします。

Clojureでこれを行うためのきれいな方法はありますか?

ありがとうございます!

+1

なぜプロトコルを使用しないのですか? – DanLebrero

+0

私は免責事項が好きでした:) – szymanowski

答えて

20

ダイナミックなバインディングが存在するため、多くの優れた用途があるため、理解してもらうための心配はありません:-)古いClojureチュートリアルでは、 ^:ダイナミックメタデータを動的に再バインドすると予想されるバースに動的メタデータ。

この最初の例では、既存の名前を再バインドして動的バインディングを使用しています。これは、新しいシンボルを導入するマクロの必要性削除:最初のいくつかの動物を作り、私はちょうどこの例ではマップを使用


を、多くの人々は、オブジェクトの他のいくつかのタイプを使用します:

(def dog {:sound #(str "wooooof")}) 
(def cat {:sound #(str "mewwww")}) 

は、関数たちを定義します

(defmacro with-animal [animal & body] 
    `(binding [speak (:sound ~animal)] 
     [email protected])) 

(defn :^dynamic speak [] (println "eh?")) 

は、動物における機能に話す結合する基本的なテンプレートのマクロを書き込む(再結合を可能にする)動的であることを再バインドします0

とそれをテスト:

(with-animal dog 
    (println (str "Dog: " (speak) "\n"))) 
Dog: wooooof             


、今ちょうど動的バインディングを必要とせずレットを使用してスコープにシンボル speakを紹介する「高度なバージョンを」。これはバインディングが何らかの形で悪いと言っているのではなく、書かないようにあなたの欲望にちょうど適合します (let-binding [speak (fn [] "meow")] ...)このタイプのマコは嫌な言葉です(あなたがそのような名字に入っている場合):

重要な部分は私はこの2つの例の間のコントラストがスコープにオブジェクトから振る舞いを結合についてのあなたの質問に答えるのに役立つことを願っています

user> (defmacro with-animal [animal & body] 
    `(let [~'speak (:sound ~animal)] 
     [email protected])) 
#'user/with-animal 

user> (with-animal dog 
     (println (str "Dog: " (speak) "\n"))) 
Dog: wooooof 

nil 


:明示的範囲に非修飾シンボルを紹介 speakシンボルの前に ~' 。最初の例は、maco本体の値とその本体から呼び出されたものをバインドします。 2番目の例では、マクロの本体に対してのみ名前を導入しています。

+0

私はこの解決が好きです。動物と一緒に使用するすべての機能を定義し、各動物ごとにそのファイルで定義する機能を定義する場所が1つあることがわかります。 –

+1

'speak'はいずれの例でも関数である必要はありませんか?これはちょうど '(def ^:dynamic speak" eh? ")'とすることができます。とにかくそれを文字列に連結するだけです。 – Ben

0

あなたが本当に、動物の種類は、慣用的に話を作るClojureのプロトコルを使用する場合:

(defprotocol ISpeak 
    (speak [animal] "make the type say it's thing")) 

(deftype Dog [] 
    ISpeak 
    (speak [this] "Woof!")) 

(deftype Cat [] 
    ISpeak 
    (speak [_] "Meow!!")) ;;you can "drop" the item if not used using _ 

(def a-dog (Dog.)) 
(speak a-dog) 
;;=>"Woof!" 

(def a-cat (Cat.)) 
(speak a-cat) 
;;=>"Meow!!" 

あなたが話す方法で任意の型(クラス)を拡張することができますのでご注意ください。

(extend java.util.Random 
    ISpeak 
    {:speak (fn [_] "I'm throwing dices at you!")}) 

(speak (java.util.Random.)) 
;;=>"I'm throwing dices at you!" 

構文はJavaクラスの少し異なっている、より多くの情報のためProtocols documentationを参照してください。