减少 Common Lisp 中的循环列表

Reducing a circular list in Common Lisp

我一直在使用 Common-lisp (SBCL) 中的循环列表,尝试在这样的列表上调用 REDUCE 时遇到以下问题。

首先,我们创建一个列表:

CL-USER> (defvar *foo* (list 1 1 1 1))
*foo*

当然可以了

CL-USER> (reduce #'+ *foo*)
4

CL-USER> (reduce #'+ *foo* :end 3)
3

但是,如果我们创建一个循环列表:

CL-USER> (setf *print-circle* t)
CL-USER> (setf (cdr (last *foo*)) *foo*)
CL-USER> *foo*
#1=(1 1 1 1 . #1#)

显然 (reduce #'+ *foo*) 现在再也不会 returns。

但是当我尝试时

 CL-USER> (reduce #'+ *foo* :end 3)
 ...

我也有一个无限循环。

为什么会这样?有没有办法在不显式使用的情况下解决这个问题 循环结构如 LOOPDO?我正在使用 SBCL,但尝试使用其他实现(CLISP、ECL),它们都有同样的问题。

我同意您观察到的行为可以作为一个开始 - 毕竟,为什么不处理循环列表的前 3 个元素?

让我们阅读一下 ANSI Common Lisp 标准:

reduce:

  • 异常情况:如果序列不是[=38],应该准备好发出type-error类型的错误信号 =]正确的顺序。

proper sequence:

  • 不是不正确列表的序列;也就是说,一个向量或一个适当的列表。

improper list:

  • 不是正确列表的列表:循环列表或点列表。

Should be prepared to signal an error:

  • 始终允许实现发出错误信号,但即使在安全代码中,也只需要在未能发出错误信号时发出错误信号,否则可能会导致错误结果。在不安全的代码中,后果是不确定的。

所以,

  • 发出错误信号是 conforming
  • 返回 3 也符合要求
  • 您的代码是 not conforming - 因为其结果无法通过阅读标准来确定
  • 如果我们考虑交互式 (REPL) 代码 safe,则无限循环 符合
  • 如果我们认为 REPL 代码不安全,则无限循环符合要求

就我个人而言,我认为交互式代码应该被视为安全的,所以这是一个错误。 YMMV.