在Common Lisp中减少循环列表

3

我是一个有用的助手,可以为您翻译文本。

我一直在使用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*)明显不再返回结果。

但是当我尝试:

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

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

2
请注意,如果您使用CCL,那么(reduce #'+ *foo* :end 3)将产生3,而(reduce #'+ *foo*)会发出一个[Condition of type CCL::IMPROPER-LIST]信号。 - Renzo
那么,了解不同系统如何检查“不当列表”应该是很有趣的。它们是否在处理之前检查整个列表?它们是否在列表类型中具有此信息?正确的语义是什么?(即使今天似乎没有人再关心编程语言的形式语义)。 - Renzo
也许SBCL的维护者会接受一个补丁,使得SBCL能够报告这样的错误? - Rainer Joswig
1个回答

5

我同意你观察到的行为可能会使人惊讶 - 毕竟,为什么不处理循环列表的前3个元素呢?

让我们阅读ANSI Common Lisp标准:

reduce

  • 异常情况:应准备好发出错误信号,如果sequence不是proper sequence,则发出类型为type-error的错误。

proper sequence

  • 序列不是improper list的序列;即向量或proper list。

improper list

  • 一个不是proper list的list:circular list或dotted list。

应准备好发出错误信号

  • 实现总是允许发出错误信号,但即使在安全代码中,只有在未能发出错误信号可能导致不正确的结果时,才需要发出错误信号。在不安全的代码中,后果是未定义的。

所以,

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

个人认为,交互式代码应被视为安全,因此这是一个bug。你的看法可能不同。


2
“但它是由ANSI Common Lisp标准强制执行的”到底意味着什么?这里肯定存在未定义的行为,实现可以自由地发出类型错误信号,尽管它们不必这样做(正如我们所看到的,许多实现似乎并没有这样做;mapcar等情况类似)。因此,我完全同意所需的行为不是标准规定的,但我不清楚您正在说强制执行的行为是什么。 - Joshua Taylor
2
然而,“这种行为(错误)是由ANSI Common Lisp标准鼓励的”实际上也有点误导,因为OP实际上并没有得到一个错误,只是一个(表面上的)无限循环。一些CL实现确实会发出错误信号(正如Renzo在评论中指出的那样)。更像是“情况具有未定义的行为,但实现可以自由地发出错误信号,即使您的实现(SBCL)没有。” - Joshua Taylor
我怀疑一个无限循环的函数调用在标准术语中并不符合"错误结果"(没有值被返回)的定义。 - Vatine
我发现一个相关的错误已经在https://bugs.launchpad.net/sbcl/+bug/309144上报告,并将此处讨论的`REDUCE`/`:END`问题作为评论发布。 - jobach
非终止函数不会返回正确的结果,因此是不正确的。 - sds

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