2011-11-14 6 views
5

ランダムに指定された式の1つを選択して評価するマクロを定義します。たとえば、Clojure:ローカルスコープで引用されたフォームを評価する方法は?

(equal-chance 
    (println "1") 
    (println "2")) 

は、半分の時間に「1」を、もう半分に「2」を出力する必要があります。

Iは

(defmacro equal-chance 
    [& exprs] 
    `(rand-nth '~exprs)) 

、使用しようとしたが、これは(すなわち、それは(println "1")を返すのではなく、実際に「1」を印刷する)むしろそれを評価するよりも、引用された形式のいずれかを返します。私はevalを使用できません。なぜなら、バインディングを保持しないからです。たとえば、

(let [x 10] (eval '(println x))) 

は、シンボルxを解決できないと訴えます。

ローカルスコープで引用されたフォームを評価する方法はありますか?それとももっと良い方法がありますか?

答えて

2

あなたはコンパイル時にのみ存在する字句環境での実行時の値を評価することはできません。解決策はfn代わりのquote、代わりにevalの関数呼び出しを使用することです:

(defmacro equal-chance [& exprs] 
    `((rand-nth [[email protected](map (fn [e] `(fn [] ~e)) exprs)]))) 

これが機能する方法は((rand-nth [(fn [] e1) (fn [] e2) ...]))(equal-chance e1 e2 ...)を拡大することです。

+0

これは素晴らしい動作します! – Ken

+0

私はfn/callではなくdelay/forceを使いたいと思います: '(force(rand-nth [〜@(map(partial list \' delay)exprs)])) ) ' – amalloy

1

rand-nthまたは式の呼び出しを引用しないでください。これは、あなたがやりたいことになります。

(defmacro equal-chance 
    [& exprs] 
    (rand-nth exprs)) 
+2

これはすべてのケースで機能するわけではありません。 '(dotimes [i 10](equal-chance 1 2 3))'は、それらの数字のうちの1つを10回返します。 – Ken

関連する問題