2010-05-15 9 views
19

Estoy intentando (como ejercicio de autoaprendizaje) crear una macro Clojure que genere código para aplicar una función a una secuencia de enteros y sumar el resultado, p.Control de la generación de símbolos en macros Clojure

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

Este es mi intento:

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

Sin embargo algo parece ir mal con el x # gensym y termino con dos versiones diferentes de x y por lo tanto la función no funciona:

(macroexpand '(testsum inc 3)) 

da:

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

Esto es más o menos lo que quiero aparte de las diferentes versiones 809 y 808 de x .....

¿Qué estoy haciendo mal? Pensé que el auto gensym estaba destinado a crear un solo símbolo único para exactamente este tipo de propósito. ¿Hay una mejor manera de hacer esto?

Respuesta

24

foo# -los gensyms de estilo son válidos solo dentro de la sintaxis-cita donde fueron creados. En su código, las dos x# s se crean en diferentes bloques de sintaxis-Cita:

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

Para solucionar este problema, utilice una explícita (gensym) llamada:

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

Y la expansión de la macro ((macroexpand '(testsum inc 3))):

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

Como una idea de último momento, también es posible reemplazar '(gensym" x ")' con '\' x # ', aunque nunca he visto a nadie hacer eso para un gensym creado explícitamente fuera del forma principal de generación de expansión. –

+0

Awesome Michal - ¡funciona perfectamente muchas gracias! Todavía estoy familiarizado con los símbolos después de venir de un mundo Java .... – mikera