8

以下は多くの理由で悪い考えだと思います。また、スタックオーバーフローの担当者が23人であることを考えると、私がプログラミングを学んでいる初心者であると想定するのが自然なことです。しかし、私をユーモアを交えて、「なぜこれをやりたいのか/やりたくないのか」という側面ではなく、「どうすればこれができるのか」に焦点を当ててください。

私が欲しいもの:

(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

したがって、基本的に、 with-animalをマクロにしたいのですが、「speak」関数呼び出しのすべての発生は、ブロックを呼び出しているオブジェクトにマップされます。

特に、私は書きたくありません:

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

むしろ、with-animalに、speak関数を呼び出しているオブジェクトのメソッドにマップさせたいと思います。

Clojureでこれを行うためのクリーンな方法はありますか?

ありがとう!

4

2 に答える 2

20

動的バインディングには理由があり、多くの優れた用途があるため、理解しようとして炎上する心配はありません:-) ^:dynamicメタデータを追加する必要がある以前の多くの古いClojureチュートリアルには混乱があります。動的に再バインドする予定の変数に。

この最初の例では、既存の名前を再バインドすることで動的バインディングを使用します。これにより、マクロで新しいシンボルを導入する必要がなくなります。


最初にいくつかの動物を作成します。この例ではマップを使用します。多くの人が他のタイプのオブジェクトを使用します。

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

再バインドする関数を動的に定義します(これにより再バインドが可能になります)

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

動物の関数に話すための基本的なテンプレートマクロを作成します。

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

そしてそれをテストします:

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


speakそして今、動的バインディングを必要とせずに、letを使用してスコープ にシンボルを導入するだけの「高度なバージョン」 。これは、バインディングが何らかの形で悪いと言っているのではなく、書きたくないというあなたの願望によりぴったり合っています。(let-binding [speak (fn [] "meow")] ...)このタイプのマコは照応と呼ばれます(あなたがそのような派手な名前に興味がある場合):

重要な部分は、修飾されていないシンボルをスコープに明示的に導入するシンボルの~'前です。speak

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

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

nil


これら2つの例の対比が、オブジェクトからスコープへの動作のバインドに関する質問に答えるのに役立つことを願っています。最初の例は、macoの本体の値と、その本体から呼び出されるすべてのものをバインドします。2番目の例では、マクロの本体にのみ名前を導入しています。

于 2012-10-08T21:46:53.797 に答える
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!!"

talkメソッドを使用して任意のタイプ(クラス)を拡張できることに注意してください。

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

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

構文はJavaクラスでは少し異なります。詳細については、プロトコルのドキュメントを参照してください。

于 2015-07-17T15:09:50.920 に答える