为什么 Common Lisp REPL 在插入这个循环列表后会一直无限运行?

问题描述

我使用的是 Common Lisp、SBCL 和 Slime。我是 Common Lisp 的新手。

显然,这是 Common Lisp 中的循环列表:

#1=('a 'b 'c . #1#)

这将提供一个无限的 'a 'b 'c 'a 'b 'c 'a...

当我把它放在 REPL 上时,它会一直运行:

CL-USER> #1=('a 'b 'c . #1#)

为什么会这样?为什么 REPL 不返回它收到的“对象”?

如果我请求列表的下一个元素,我可以理解无限行为。但是,我向 REPL 询问了对象本身。

我期待与正确列表或点列表相同的行为:

CL-USER> (list 'a 'b 'c)
(A B C)

CL-USER> (cons 'a  (cons 'b 'c))
(A B . C)

解决方法

想一想打印这样的对象涉及什么:一台普通的打印机需要多长时间来打印它以及输出有多大?

好吧,CL 可以让您解决这个问题:有许多打印机控制变量,其中最直接有用的可能是 *print-circle*。您可以将此设置为 true 以在打印机中启用圆形检测。如果您这样做,您可能还会意识到为什么默认情况下它是 nil

,

如果我要求列表的下一个元素,我可以理解无限行为。

为什么?下一个元素不是具有无限计算时间的操作。它只是获取下一个元素,这只是一个简单的操作。它只是循环列表中的下一个元素。

无限是在循环中请求下一个元素,检查列表的末尾。

(cdr list)

对比

(dolist (e list)
  ...)

可能打印机(打印结果的读取评估打印循环的一部分)在想要打印无限列表的元素时会做类似的事情。 TFB 提到需要特殊检查来检测循环性以保持计算有界。