在Clojure中是否有办法编写一个返回两个或更多s表达式的宏(或使用现有的宏)?
实施例。假设我正在尝试生成前10个方块:
(map * (range 10) (range 10))
现在我想尝试使用重复,这样我以后可以提供电源(现在为2)作为参数:
(map * (repeat 2 (range 10)))
然而上述情况不起作用,因为重复会返回一个带有两个范围的s-expression,而不是两个范围。
请不要提供“返回前10个方格”功能的替代实现。我对处理此类情况或一般适用的解决方法的惯用方法感兴趣。
我想我正在寻找一种方法来实施爆炸:
(explode '( a b c )) ;; evals to a b c
(map * (explode (repeat 2 (range 10)))
答案 0 :(得分:4)
单个宏无法做到这一点。
宏是一个函数,采用s表达式并将其转换为另一个s表达式。输入可以是任何表达式序列(符号,灵长类,其他表达式等)和输出是插入的单个表达式代替原始宏调用。
一般来说,宏必须围绕调用地图并转换整个表单
(your-macro-here (map * (repeat 2 (range 10))))
如果你有两个宏,一个产生表达式,另一个包含整个外部形式,外部宏也许可以找到包含宏结果的单个表达式,在你的例子中有两个范围,并将它们提升到一个级别独立的表达。我不建议在实践中这样做,它只是用来说明宏的功能。
答案 1 :(得分:2)
正如亚瑟所说,你不能完全用宏来寻找你正在寻找的东西。可以使用变通方法,具体取决于您要对这些表单执行的操作。
如果您只是想评估宏生成的多种形式,例如为了简化样板代码,您可以用do
形式包装这些多个表单:
(defmacro defn-foos [& foos]
`(do ~@(map (fn [foo] `(defn ~foo [])) foos)))
(defn-foos my-fn-1 my-fn-2)
;; expands as:
;; (do (defn my-fn-1 [])
;; (defn my-fn-2 []))
如果您想在函数调用中使用展开的表单作为位置参数,就像您看到的那样,那么您最好的选择可能是将它们包装在list
中,然后使用apply
将列表解压缩为函数的位置参数。
要了解其工作原理,请注意原始问题中的第二个示例可以通过apply
进行简单修改,以便工作:
(apply map * (repeat 2 (range 10)))