有没有办法让宏在返回结果之前进行额外的评估?

Knu*_*uto 5 macros common-lisp control-flow

我试图让我的宏在返回结果之前对其结果做额外的评估。可以不这样做eval吗?

我正在尝试解决以下练习4中的问题:

  1. 定义一个宏nth-expr,它接受整数n和任意数量的表达式,计算第n个表达式并返回其值。如果您假设第一个参数是文字整数,则此练习很容易解决。

4.作为练习3,但假设第一个参数是要求值的表达式。

让宏选择正确的表达式很容易:

(defmacro nth-expr% (n &rest es)
  `(nth ,n ',es))

CL-USER> (defvar i 1)
I
CL-USER> (nth-expr% (1+ i) (+ 2 3) (- 4 3) (+ 3 1))
(+ 3 1)
Run Code Online (Sandbox Code Playgroud)

该表达式(+ 3 1)是我们想要的表达式,但是我们希望宏在返回它之前将其求值为4。

当然可以用eval完成:

(defmacro nth-expr%% (n &rest es)
  `(eval (nth ,n ',es)))

CL-USER> (nth-expr%% (1+ i) (+ 2 3) (- 4 3) (+ 3 1))
4
Run Code Online (Sandbox Code Playgroud)

但是还有另一种方法吗?

感觉解决方案应该是将其主体nth-expr%放在帮助程序宏中,并使顶层宏仅包含对此帮助程序的未引用调用:

(defmacro helper (n es)
  `(nth ,n ',es))

(defmacro nth-expr (n &rest es) ; doesn't work!
  (helper n es))
Run Code Online (Sandbox Code Playgroud)

这样的想法是,对to的调用helper将返回(+ 3 1),然后这将是对to的调用的扩展nth-expr,在运行时将求值为4。它膨胀了,当然是因为N并且ES像对待文字一样对待。

Rai*_*wig 8

那不是那么容易。

使用eval效果不好,因为eval它不会在本地词汇环境中评估代码。

请记住,如果我们允许对一个表达式求值以确定要执行的另一个表达式的数量,那么在宏扩展时我们就不知道这个数字了-因为该表达式可能基于需要计算的值-例如基于一些变量:

(nth-expression
   foo
 (bar)
 (baz))
Run Code Online (Sandbox Code Playgroud)

因此,我们可能想考虑执行此操作的代码:

(case foo
  (0 (bar))
  (1 (baz)))
Run Code Online (Sandbox Code Playgroud)

CASE正在评估foo,然后使用结果查找在其头部具有相同值的子句。然后将评估该子句的后续形式。

现在,我们需要编写将前者扩展为后者的代码。

这将是一个非常简单的版本:

(defmacro nth-expression (n-form &body expressions)
  `(case ,n-form
     ,@(loop for e in expressions
             and i from 0
             collect `(,i ,e))))
Run Code Online (Sandbox Code Playgroud)

问题:那样使用可能会有什么弊端CASE

  • 我可以看到运行时性能可能会很差,因为评估第300个表达式将涉及在找到匹配项之前对`eql`进行300次调用。 (2认同)
  • @Knuto:对。线性搜索就是问题所在。通常,我们会认为,只有很少的选择,这可能不是真正的问题。但是我们也要记住,在Lisp中,代码很容易计算,并且可能产生很多选择。 (2认同)