2

这是一个符合标准的 Common Lisp 程序吗?

(handler-bind ((condition (let ((x 0))
                            (lambda (c)
                              (declare (ignore c))
                              (print (incf x))))))
  (signal 'condition)
  (signal 'condition))

SBCL (2.0.5.37) 的输出是:

1
1

ABCL/CCL/ECL 的输出是:

1
2

Common Lisp 标准定义了哪种行为?


结语

这是 SBCL 中的一个错误,现在已修复

4

1 回答 1

3

这并不完全清楚。规范说:

在指定的处理程序绑定有效的动态环境中执行表单

然后说

如果找到合适的类型,则关联的处理程序将在动态环境中运行,其中这些处理程序绑定都不可见(以避免递归错误)。

如果您将“运行”的含义解释为调用该函数,则表明处理程序表达式在进行绑定时被评估一次。这是 CCL/ABCL/ECL/LispWorks 的实现,所以在闭包中维护状态。

但 SBCL 似乎将“运行”解释为“评估和调用”的意思。因此,每次运行处理程序时都会创建一个新的闭包,并且状态会丢失。

我怀疑意图是第一个解释,因为 CL 没有其他“惰性”绑定。

如果您将问题中的代码更改为:

(let ((handler
        (let ((x 0))
          (lambda (c)
            (declare (ignore c))
            (print (incf x))))))
  (handler-bind ((condition handler))
    (signal 'condition)
    (signal 'condition)))

那么 SBCL 的行为方式与其他实现相同。我认为这很清楚地表明,其他实现所采用的解释是预期的,并且它还提供了一个实用的解决方法,如果该解释实际上是正确的,那么它是 SBCL 中的一个错误。

于 2021-02-19T18:20:38.740 回答