5

Clojure 中有没有办法编写一个返回两个或多个 s 表达式的宏(或使用现有的宏)?

例子。假设我正在尝试生成前 10 个正方形:

(map * (range 10) (range 10))

现在我想尝试使用重复,以便以后可以提供功率(现在为 2)作为参数:

(map * (repeat 2 (range 10)))

但是,上述内容不起作用,因为repeat返回具有两个范围而不是两个范围的单个 s 表达式。

请不要提供“返回前 10 个方块”功能的替代实现。我对处理此类情况的惯用方法或普遍适用的解决方法感兴趣。

我想我正在寻找一种在下面实现爆炸的方法:

(explode '( a b c )) ;; evals to a b c
(map * (explode (repeat 2 (range 10)))
4

2 回答 2

4

单个宏无法做到这一点。

宏是一个函数,它接受一个 s 表达式并将其转换为另一个 s 表达式。输入可以是任何表达式序列(符号、原语、其他表达式等),输出是插入代替原始宏调用的单个表达式。

一般来说,宏必须围绕调用 map 和转换整个表单

(your-macro-here (map * (repeat 2 (range 10))))

如果您有两个宏,一个生成表达式,另一个包含整个外部形式,则外部宏可能会找到包含宏结果的单个表达式,在您的示例中为两个范围,并将它们提升一层以成为独立表达式. 我不建议在实践中这样做,它只是为了说明宏的功能。

于 2013-04-03T20:37:19.020 回答
3

正如 Arthur 所说,你不能完全用宏来做你正在寻找的东西。变通方法是可能的,具体取决于您要对这些表单执行的操作。

如果您只是想评估宏生成的多个表单,例如为了简化样板代码,您可以将这些多个表单包装在一个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 []))

如果您想在函数调用中使用扩展形式作为位置参数,就像您所做的那样,那么您最好的选择可能是将它们包装在 a 中list,然后使用applywhich 解包列表作为函数的位置参数。

要了解这是如何工作的,请注意原始问题中的第二个示例可以使用以下方法进行简单修改apply

(apply map * (repeat 2 (range 10)))
于 2013-04-03T21:08:21.273 回答