19 votos

Controlar la generación de símbolo en Clojure macros

Estoy tratando (como un ejercicio de autoconocimiento) para crear un Clojure macro que va a generar el código para aplicar una función a una secuencia de enteros y suma el resultado, por ejemplo,

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

Este es mi intento:

(defmacro testsum [func n] 
  `(fn [x#] (+ ~@( 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 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 casi exactamente lo que quiero, aparte de los diferentes 808 809 y las versiones de x.....

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

24voto

Michał Marczyk Puntos 54179

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

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

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

(defmacro testsum [func n]
  (let [x (gensym "x")]
    `(fn [~x] (+ ~@(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))))

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X