减少Common Lisp中的循环列表

job*_*ach 3 reduce list sbcl common-lisp circular-list

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

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

CL-USER> (defvar *foo* (list 1 1 1 1))
*foo*
Run Code Online (Sandbox Code Playgroud)

当然,现在我们可以做到

CL-USER> (reduce #'+ *foo*)
4
Run Code Online (Sandbox Code Playgroud)

要么

CL-USER> (reduce #'+ *foo* :end 3)
3
Run Code Online (Sandbox Code Playgroud)

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

CL-USER> (setf *print-circle* t)
CL-USER> (setf (cdr (last *foo*)) *foo*)
CL-USER> *foo*
#1=(1 1 1 1 . #1#)
Run Code Online (Sandbox Code Playgroud)

显然(reduce #'+ *foo*)现在永远不会回来.

但是当我尝试的时候

 CL-USER> (reduce #'+ *foo* :end 3)
 ...
Run Code Online (Sandbox Code Playgroud)

我也有一个无限循环.

为什么会这样?有没有办法解决这个问题,而没有明确使用循环结构,如LOOPDO?我正在使用SBCL,但尝试使用其他实现(CLISP,ECL),它们都有同样的问题.

sds*_*sds 5

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

让我们阅读ANSI Common Lisp标准:

reduce:

  • 特殊情况:如果序列不是正确的序列,应准备好发出类型错误信号.type-error

正确的顺序:

  • 一个不是不正确列表的序列; 即,向量或适当的列表.

不正确的清单:

  • 列表不是正确的列表:循环列表或虚线列表.

应准备好发出错误信号:

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

所以,

  • 发信号错误符合要求
  • 返回3也符合要求
  • 你的代码不符合 - 因为它的结果不能通过阅读标准来确定
  • 如果我们认为交互式(REPL)代码是安全的,那么无限循环就不符合要求
  • 如果我们认为REPL代码不安全,那么无限循环就符合要求

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

  • 究竟是什么意思:"但它是由ANSI Common Lisp标准强制执行的"?这里肯定存在未定义的行为,并且这些实现可以自由地发出类型错误的信号,即使它们没有必要(正如我们所见,很多似乎没有;类似于mapcar等).所以我完全同意标准所要求的行为不是*,但我不清楚你所说的*是*强制性的. (2认同)
  • 尽管如此,"ANSI Common Lisp标准鼓励这种行为(错误)"实际上也有点误导,因为OP*实际上并没有实现错误,只是一个(明显的)无限循环.一些CL实现确实发出错误信号(正如Renzo在[评论]中指出的那样(http://stackoverflow.com/questions/32010174/reducing-a-circular-list-in-common-lisp/32012790?noredirect=1#comment51928895_32010174 )).它更像是"情况具有未定义的行为,但实现可以自由发出错误信号,即使您的实现(SBCL)没有." (2认同)