宏在多种语法引用形式中重用自动gensym

时间:2014-01-06 00:52:41

标签: clojure

我编写了一个如下所示的宏,它按预期工作,记录在运行时传递给函数的参数。

(use 'robert.hooke)

(defmacro log-func-args [func arg-log-option print-suffix]
  (condp = arg-log-option
    :all   `(add-hook (var ~func) (fn [f# & args#]
                                   (println "****** " (:name (meta (var ~func))) " called with "
                                         ~print-suffix args#)
                                   (apply f# args#) ))

   :first  `(add-hook (var ~func) (fn [f# & args#]
                                   (println "****** " (:name (meta (var ~func))) " called with "
                                         ~print-suffix (first args#))
                                   (apply f# args#) ))

   :rest  `(add-hook (var ~func) (fn [f# & args#]
                                   (println "****** " (:name (meta (var ~func))) " called with "
                                         ~print-suffix (rest args#))
                                   (apply f# args#) ))
   )  )

然后使用它:

(log-func-args my-func-name :rest "[server,buyer]: ")

上面的宏看起来有点冗长,所以我试着重构它,以便代替多个add-hook,我只有一个内部卷入了condp,就像这样:

(defmacro log-func-args [func arg-log-option print-suffix]
 `(add-hook (var ~func) (fn [f# & args#]
                       ~(condp = arg-log-option
                          :all    `(println "****** " (:name (meta (var ~func))) " called with "
                                        ~print-suffix args#)
                          :first  `(println "****** " (:name (meta (var ~func))) " called with "
                                           ~print-suffix (first args#))
                          :rest   `(println "****** " (:name (meta (var ~func))) " called with "
                                         ~print-suffix (rest args#))                              
                          )

                       (apply f# args#)  ))

这给了我一个RuntimeException,抱怨println中的args#无法解析(因为两个语法引用形式的args#生成的符号 - 加钩形式和println形式 - 不一样) 。什么是干净的补救措施?

2 个答案:

答案 0 :(得分:0)

使用带有let的gensym func找到一个解决方案(由Clojure编程提供)。这允许语法引用的两种形式共享相同的args符号。希望这有助于其他人。很想看到任何其他解决方案。

(defmacro log-func-args2 [func arg-log-option print-suffix]
  (let [args (gensym "args")]
    `(add-hook (var ~func) (fn [f# & ~args]
                              ~(condp = arg-log-option
                                 :all    `(println "****** " (:name (meta (var ~func))) " called with "
                                              ~print-suffix ~args)
                                 :first  `(println "****** " (:name (meta (var ~func))) " called with "
                                              ~print-suffix (first ~args))
                                 :rest   `(println "****** " (:name (meta (var ~func))) " called with "
                                              ~print-suffix (rest ~args)) )

                              (apply f# ~args)  )) )  )

答案 1 :(得分:0)

看一下potemkin的unify-gensyms功能。它允许您使用两个哈希值(##)而不是一个哈希值来表示应该用相同生成的符号替换的值。

跨越多种形式的gensyms越多,此解决方案就越好(与明确的let / gensym形式相比)。