由于对 clojure 及其宏很感兴趣,我给自己设定了一个任务:编写一个宏,从字符列表(例如“abc”)生成所有长度为“n”的字符串。因此,对于 n=2,输出应为“aa”“ab”“ac”“ba”“bb”“bc”“ca”“cb”“cc”。我从以下函数作为模板开始: (defn mkstr [n v] (for [i v j v] (str i j)))。 for 绑定(bind)中“v”的重复以及多少个变量的创建应该是“n”的函数;在本例中:2.
在阅读了“quote”“unquote”等内容之后,然后遵循了有关宏的优秀在线教程,进行了多次尝试和错误,并且运气不错,我成功地生成了以下函数和宏,无论什么,它们都能提供所需的输出'n' 的值。真正困难的部分是生成“for”绑定(bind)中所需的可变数量的代码。
(defn mkvars [n]
"Gives a list of 'n' unique symbols"
(let [vc (repeatedly n #(gensym ))] vc))
(defmacro mkcoms [n syms]
"Generates a list of possible combinations of length 'n' from a string of symbols"
`(let [vs# (mkvars ~n) sy# ~syms
forarg# (vec (interleave vs# (repeat ~n sy#)))]
`(for ~forarg# (str ~@vs#))))
现在我的“真正”问题或缺乏理解是为了获得输出我必须这样做: (评估(mkcoms len chars))。为什么这只能通过使用“eval”才能起作用?确实,它可以按原样使用,但感觉有些不对劲。
最佳答案
您的宏返回带引号的形式,这就是为什么当您将其传递给 eval 时它会起作用。我不明白宏相对于函数的目的是什么,所以我希望这个解释是您想要的。
宏应该生成它所代表的代码并返回它。您的宏生成一个引用的形式。如果您删除反引用的外层,这看起来是为了成为执行扩展的代码(宏)而不是结果代码的一部分,那么您确实可以在宏扩展时执行:
(defmacro mkcoms [n syms]
"Generates a list of possible combinations of length 'n' from a string of symbols"
(let [vs (mkvars n)
sy syms
forarg (vec (interleave vs (repeat n sy)))]
`(for ~forarg (str ~@vs))))
这听起来像是您所追求的,尽管我承认我不明白为什么您希望这种情况发生在“编译时”而不是运行时。
关于Clojure宏评估,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13660870/