2011-11-14 7 views
5

Voglio definire una macro che sceglie casualmente una delle espressioni specificate e la valuta. Ad esempio,Clojure: come valutare un modulo quotato nell'ambito locale?

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

deve stampare "1" metà del tempo e "2" l'altra metà.

Ho provato ad utilizzare,

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

ma questo restituisce una delle forme citate, piuttosto che valutarla (vale a dire che sarebbe tornato (println "1") piuttosto che in realtà la stampa di "1"). Non riesco a utilizzare eval perché non conserva i binding. Ad esempio,

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

si lamenta che non è in grado di risolvere il simbolo x.

Esiste un modo per valutare un modulo quotato nell'ambito locale? O forse c'è un modo migliore per farlo?

risposta

2

Non si può valutare un valore di run-time in un ambiente lessicale che esiste solo in fase di compilazione. La soluzione è quella di utilizzare fn anziché quote e una chiamata di funzione invece di eval:

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

il modo in cui funziona è espandendo (equal-chance e1 e2 ...) in ((rand-nth [(fn [] e1) (fn [] e2) ...])).

+0

funziona alla grande! non l'avrei mai capito da solo, grazie – Ken

+0

Preferisco usare delay/force piuttosto che fn/call per questo: '(force (rand-nth [~ @ (map (lista parziale \' delay) exprs)]))) ' – amalloy

1

Basta non citare la chiamata a rand-nth o le espressioni. Questo farà quello che vuoi:

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

Questo non funziona in tutti i casi. '(dotimes [i 10] (equal-chance 1 2 3))' restituirà uno di quei numeri 10 volte. – Ken

Problemi correlati