在Common Lisp中,为什么宏OR要使用gensym,而AND不需要?

10
在Common Lisp(SBCL 1.0.58)中,为什么OR宏使用gensym,而AND不使用呢?
例如:
    CL-USER> (macroexpand '(and 1 2 3 4 5))
    (IF 1
        (AND 2 3 4 5)
        NIL)
    T
    CL-USER> (macroexpand '(or 1 2 3 4 5))
    (LET ((#:G967 1))
      (IF #:G967
          #:G967
          (OR 2 3 4 5)))
    T
    CL-USER> 

我查看了defboot.lisp文件中定义宏的部分,但在注释中没有找到任何相关内容。

2个回答

17

这是因为实现的逻辑运算符旨在进行短路计算并返回它们评估的最后一个表单生成的值。

为了实现这一点,and 不需要 gensym,因为它评估的最后一个表单要么生成 NIL,要么是对自身的最终尾调用的结果。

另一方面,or 必须返回它评估的第一个非-NIL 值,因此它不能依赖于尾调用。它需要一个 gensym 来实现这一点,因为如果没有一个 gensym

(IF 1
    1
    (OR 2 3 4 5))

"1"在这个展开式中出现了两次,而在我们的情况下,这意味着产生"1"的表达式被计算了两次而你永远不希望在宏中出现这种情况

是的,我现在明白了。谢谢。 - kes

4

假设a是false,但是bcd都是true。现在,由于短路效应,我们有:

(or  a b c d) => b
(and a b c d) => nil
(or    b c d) => b
(and   b c d) => d

正如您所看到的,在AND的情况下,除非只有一个参数(此时扩展不同),否则最左边参数的值永远不会作为返回形式的返回值。 而在OR的情况下,如果最左边的参数为真,则其值将作为返回值。 因此,AND可以在测试真实性之后丢弃该值(因此无需将其存储在临时变量中),但OR则不能这样做。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接