我从以下代码开始(想象不止这些,但我认为这得到了重点):
(defn fun1 [arg] {:fun1 arg})
(defn funA [arg] {:funA arg})
(defn funOne [arg] {:funOne arg})
(defn funBee [arg] {:funBee arg})
(defn -main [& args] (prn (fun1 "test-data")))
我的下一个传递就是这样:
(defmacro item-defn [a]
`(defn ~(symbol a) [arg#] {~(keyword a) arg#}))
(item-defn "fun1")
(item-defn "funA")
(item-defn "funOne")
(item-defn "funBee")
(defn -main [& args] (prn (fun1 "test-data")))
有没有办法将其归结为:
(defmacro item-defn [a]
`(defn ~(symbol a) [arg#] {~(keyword a) arg#}))
(map #(item-defn %) ["fun1" "funA" "funOne" "funBee"])
(defn -main [& args] (prn (fun1 "test-data")))
(我在repl中尝试过,它似乎有效,但是当我在其中加载一个clj文件时,它就不起作用。它给了我一个" CompilerException" "无法解析符号:fun1")
我是否误用了宏?你会怎么做?
答案 0 :(得分:5)
您可以为此目的定义另一个宏,例如:
(defmacro item-defn [a]
`(defn ~(symbol a) [arg#] {~(keyword a) arg#}))
(defmacro items-defn [& names]
`(do ~@(for [n names] `(item-defn ~n))))
然后您就可以使用它来定义任意数量的函数:
(items-defn "fun1" "funA" "funOne" "funBee")
答案 1 :(得分:1)
我想知道你map
表达式是否真的在REPL中起作用。我怀疑您的fun1
和funA
函数仍然在您的REPL中,因为您首先评估了(item-defn "fun1")
和(item-defn "funA")
。在我的盒子上,我得到:
(map #(item-defn %) ["fun1" "funA"])
;=> (#'user/p1__22185# #'user/p1__22185#)
因此,没有使用名称fun1
或funA
定义任何功能。问题是map
是一个函数,item-defn
是一个宏。在map
epxression中发生的事情是item-defn
在编译时被宏扩展,此时具有函数名称的字符串不可见。宏扩展程序无法知道您希望使用"fun1"
作为defn
- ed函数的名称。相反,宏扩展器只看到%
,然后使用gen-symed名称作为defn
-ed函数的名称。 map
表达式是在运行时计算的,但是宏扩展函数对提供的字符串执行任何操作都为时已晚。
Leonid的解决方案有效,因为他使用另一个宏来迭代函数名称。这样迭代也会在编译时发生。你看,宏有点具有传染性。一旦你开始,你就无法停止。
答案 2 :(得分:0)
在宏中,名称已经是符号,因此您可以执行以下操作:
(defmacro item-defn [name]
`(defn ~name [arg#] {~(keyword name) arg#}))
然后
(item-defn fun1)