【发布时间】:2019-03-15 03:38:31
【问题描述】:
我正在尝试编写一个宏来创建任意数量的嵌套循环并在循环的每次迭代中执行一些代码。在我的第一次尝试中(如下所示),宏返回了代码而不是运行它。
;; WRONG! Returns a bunch of nested loops instead of evaluating the code.
(defmacro do-combinations ((var lists) &body body)
`(let* ((lst (mapcar #'(lambda (x)
`(loop for ,(gensym) in (list ,@x) do))
,lists))
(symbols (mapcar #'caddr lst)))
(reduce #'(lambda (x y) `(,@y ,x))
lst
:initial-value `(let ((,',var (list ,@symbols)))
(progn ,',@body)))))
CL-USER 25 : 1 > (do-combinations (n '((1 2 3)
(10 20 30)
(100 200 300)))
(pprint n))
(LOOP FOR #:G872 IN (LIST 100 200 300)
DO (LOOP FOR #:G871 IN (LIST 10 20 30)
DO (LOOP FOR #:G870 IN (LIST 1 2 3)
DO (LET # #))))
为此,我最后的解决办法是插入eval
;; Ugly fix with eval
(defmacro do-combinations ((var lists) &body body)
`(let* ((lst (mapcar #'(lambda (x)
`(loop for ,(gensym) in (list ,@x) do))
,lists))
(symbols (mapcar #'caddr lst)))
(eval (reduce #'(lambda (x y) `(,@y ,x))
lst
:initial-value `(let ((,',var (list ,@symbols)))
(progn ,',@body))))))
CL-USER 35 : 1 > (do-combinations (n '((1 2 3)
(10 20 30)
(100 200 300)))
(pprint n))
(1 10 100)
(2 10 100)
...
修复确实有效(有点),但看起来很糟糕。如果不求助于eval,如何更优雅地编写这个宏?
【问题讨论】:
-
这是一种锻炼,还是你真的只需要
alexandria:map-product? -
如果您在 LispWorks 中看到类似 'CL-USER 35 : 1 >' 的提示,那么这意味着您处于错误中断循环中。输入
:top会让你摆脱困境。通常使用它是没有意义的——它是为了在发生错误时进行调试。 -
@Svante,我只是认为嵌套循环的宏会很酷。宏的重点是运行所有组合,而不会像
alexandria:map-product那样存储太多中间值。 -
@PauloMendes:可以,但是这样的宏需要知道宏展开时嵌套循环的次数,所以基本上只能和常量一起工作。
标签: macros common-lisp eval