2010-05-15 4 views
19

Sto provando (come esercizio di autoapprendimento) per creare una macro Clojure che genererà codice per applicare una funzione a una sequenza di numeri interi e sommare il risultato, ad es.Controllo della generazione di simboli in macro Clojure

f (0) + f (1) + f (2) + f (3)

Questo è il mio tentativo:

(defmacro testsum [func n] 
    `(fn [x#] (+ [email protected](map (fn [i] `(~func x#)) (range n))))) 

Tuttavia qualcosa sembra andare storto con la x # gensym e io alla fine con due differenti versioni di x e quindi la funzione non funziona:

(macroexpand '(testsum inc 3)) 

dà:

(fn* ([x__809__auto__] 
    (clojure.core/+ 
    (inc x__808__auto__) 
    (inc x__808__auto__) 
    (inc x__808__auto__)))) 

Questo è praticamente esattamente ciò che voglio a parte le diverse versioni 809 e 808 di x .....

Cosa sto facendo male? Pensavo che l'auto gensym fosse pensata per creare un unico simbolo unico esattamente per questo tipo di scopo? C'è un modo migliore per farlo?

risposta

24

foo# gensyms in stile sono validi solo all'interno della citazione sintassi in cui sono stati creati. Nel codice, le due x# s vengono creati in diversi blocchi di sintassi-citazione:

(defmacro testsum [func n] 
    `(fn [x#] (+ [email protected](map (fn [i] `(~func x#)) (range n))))) 
    ^- s-q1  ^-unquote  ^- s-q2 

Per risolvere questo problema, utilizzare un esplicito (gensym) chiamata:

(defmacro testsum [func n] 
    (let [x (gensym "x")] 
    `(fn [~x] (+ [email protected](map (fn [i] `(~func ~x)) (range n)))))) 

e la macro di espansione ((macroexpand '(testsum inc 3))):

(fn* ([x4966] (clojure.core/+ (inc x4966) (inc x4966) (inc x4966)))) 
+3

Come ripensamento, è anche possibile sostituire '(gensym" x ")' con '\' x #', anche se non ho mai visto nessuno farlo per un gensym creato esplicitamente al di fuori del forma generatrice di espansione principale. –

+0

Awesome Michal - funziona perfettamente grazie molte! Continuando a capirmi i simboli dopo essere venuto da un mondo Java .... – mikera