首页 > 解决方案 > Common Lisp 执行表达式作为宏中的参数

问题描述

因此,使用 common lisp,我希望能够执行以下操作:

(defmacro foo (count &rest someExpression)
    `(do
        ((,count 0 (+ ,count 1)))
        ((= ,count 5) T)
        `(eval ,someExpression)
    )
)
(foo (print 1) temp)

结果打印 1 5 次。我不想简单地直接调用 (print 1),而是通过宏参数传递表达式并通过宏调用它。换句话说,宏 foo 应该处理任何表达式作为输入并运行它。这种情况似乎不起作用。

编辑以阐明明确的脚本和预期功能。

标签: lispcommon-lisplisp-macros

解决方案


从您的最新版本开始,与旧版本不同,这至少是宏的合理候选者:

(defmacro foo (someExpression count-var)
  `(do ((,count-var 0 (+ ,count 1)))
       ((= ,count-var 5) T)
     `(eval (,someExpression))))

那么什么是扩展(foo (print 1) c)

(foo (print 1) x)
 -> (do ((x 0 (+ x 1))) ((= x 5) t)
      `(eval (,someexpression)))

好吧,那是一场灾难:嵌套的反引号在做什么?让我们删除它:

(defmacro foo (someExpression count-var)
  `(do ((,count-var 0 (+ ,count 1)))
       ((= ,count-var 5) T)
     (eval (,someExpression))))
(foo (print 1) x)
 -> (do ((x 0 (+ x 1))) ((= x 5) t)
      (eval ((print 1))))

这不那么灾难性,但eval形式完全是虚假的。我们可以通过将其更改为至少在语法上合法来使其“工作”:

(defmacro foo (someExpression count)
  `(do ((,count 0 (+ ,count 1)))
       ((= ,count 5) T)
     (eval ,someExpression)))

现在

(foo (print 1) x)
 -> (do ((x 0 (+ x 1))) ((= x 5) t)
      (eval (print 1)))

这将“起作用”,但纯粹是巧合:因为(print 1)返回1和值1is 1

(foo (print 'foo) x)
  -> (do ((x 0 (+ x 1))) ((= x 5) t)
       (eval (print 'foo)))

这是一个运行时错误。

但是......你为什么要使用evaleval对于您能想到的几乎所有问题,它都是一个糟糕的、糟糕的解决方案,除非该问题的解决方案被称为“代码注入攻击”,在这种情况下,它不仅糟糕,而且是错误的。所以我们只是删除它。

(defmacro foo (someExpression count)
  `(do ((,count 0 (+ ,count 1)))
       ((= ,count 5) T)
     ,someExpression))

现在

(foo (print 'foo) x)
 -> (do ((x 0 (+ x 1))) ((= x 5) t)
      (print 'foo))

这看起来像我们想要的代码转换。所以,最后:

> (foo (print 'foo) x)

foo 
foo 
foo 
foo 
foo 
t

最后,这很好。这有效:

> (foo (print x) x)

0 
1 
2 
3 
4 
t

与对问题的另一个编辑一样,将变量名放在第一位并允许使用一堆表达式可能更有用:

(defmacro foo (count-var &body forms)
  `(do ((,count-var 0 (+ ,count-var 1)))
       ((= ,count-var 5))
     ,@forms))

这现在将允许在正文中使用多个表达式。我们可以更进一步:我们可以允许它指定迭代次数和返回值`:

(defmacro foo ((count-var &optional (count 1) (value 'nil)) &body forms)
  `(do ((,count-var 0 (1+ ,count-var)))
       ((= ,count-var ,count) ,value)
     ,@forms))

现在

> (foo (x 2)
    (print x)
    (print (* x 2)))

0 
0 
1 
2
nil

嗯,这个宏的名字dotimes当然是。


推荐阅读