Подтвердить что ты не робот

Управление генерацией символов в макросах Clojure

Я пытаюсь (как упражнение самообучения) создавать макрос Clojure, который будет генерировать код для применения функции к последовательности целых чисел и суммировать результат, например.

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

Это моя попытка:

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

Однако что-то кажется неправильным с x # gensym, и я заканчиваю двумя разными версиями x и, следовательно, функция не работает:

(macroexpand '(testsum inc 3))

дает:

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

Это в значительной степени то, что я хочу, кроме разных 809 и 808 версий x.....

Что я делаю неправильно? Я думал, что авто gensym должен был создать единственный уникальный символ для такого рода целей? Есть ли лучший способ сделать это?

4b9b3361

Ответ 1

foo# -style gensyms действительны только внутри цитаты синтаксиса, где они были созданы. В вашем коде два x# создаются в разных блоках синтаксиса-цитаты:

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

Чтобы исправить это, используйте явный вызов (gensym):

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

И расширение макроса ((macroexpand '(testsum inc 3))):

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