Common lisp SETF既不终止也不报告错误

Common lisp SETF既不终止也不报告错误,common-lisp,setf,Common Lisp,Setf,我是一个普通的Lisp初学者,遇到了以下代码: (let ((foo (list 42))) (setf (rest foo) foo)) 当尝试执行REPL时,它似乎一直在循环。什么是FOO? FOO最初是一个新列表,(42)。在Lisp中,列表由,个可变内存块表示,每个可变内存块包含一个CAR和一个CDR插槽。 另一种打印方法是(42.NIL),其中汽车和CDR位于点的两侧。这也可以如下所示: car cdr ------------ | 42 | NIL | ---------

我是一个普通的Lisp初学者,遇到了以下代码:

(let ((foo (list 42)))
  (setf (rest foo) foo))
当尝试执行REPL时,它似乎一直在循环。

什么是
FOO
FOO
最初是一个新列表,
(42)
。在Lisp中,列表由,个可变内存块表示,每个可变内存块包含一个
CAR
和一个
CDR
插槽。 另一种打印方法是
(42.NIL)
,其中
汽车
CDR
位于点的两侧。这也可以如下所示:

  car  cdr
------------
| 42 | NIL |
------------
     ^
     |
    FOO
当您使用
(rest-foo)
位置和
foo
值调用时,您的意思是希望
foo
的cdr单元格保存值
foo
。事实上,
SETF
的出现很可能会扩展为对的调用

为什么REPL会永远循环? “REPL”(print)的“p”部分试图打印您的循环结构。这是因为
SETF
的值是从正在计算的表单返回的值,而
SETF
返回的值是其第二个参数的值,即
FOO
。假设您想用一个简单的算法编写一个cons单元X:

1. PRINT "("
2. PRINT the CAR of X
3. PRINT " . "
4. PRINT the CDR of X
5. PRINT ")"
但是,对于
foo
,步骤4将以递归方式打印相同的结构,并且永远不会终止

你能做什么? 首先尝试设置为T:

(setf *print-circle* t)
现在,您的回复应该很高兴:

CL-USER> (let ((foo (list 42)))
           (setf (rest foo) foo))
#1=(42 . #1#)
该符号允许读取器将表单的一部分影响到(读取器)变量,如
\1=…
,并在以后重用它,例如
\1
。这使得在读取或打印期间可以表示数据之间的循环交叉引用。在这里,我们可以看到变量
#1#
表示cons单元格,其中
CAR
是42,
CDR
#1#
本身。

什么是
FOO
FOO
最初是一个新列表,
(42)
。在Lisp中,列表由,个可变内存块表示,每个可变内存块包含一个
CAR
和一个
CDR
插槽。 另一种打印方法是
(42.NIL)
,其中
汽车
CDR
位于点的两侧。这也可以如下所示:

  car  cdr
------------
| 42 | NIL |
------------
     ^
     |
    FOO
当您使用
(rest-foo)
位置和
foo
值调用时,您的意思是希望
foo
的cdr单元格保存值
foo
。事实上,
SETF
的出现很可能会扩展为对的调用

为什么REPL会永远循环? “REPL”(print)的“p”部分试图打印您的循环结构。这是因为
SETF
的值是从正在计算的表单返回的值,而
SETF
返回的值是其第二个参数的值,即
FOO
。假设您想用一个简单的算法编写一个cons单元X:

1. PRINT "("
2. PRINT the CAR of X
3. PRINT " . "
4. PRINT the CDR of X
5. PRINT ")"
但是,对于
foo
,步骤4将以递归方式打印相同的结构,并且永远不会终止

你能做什么? 首先尝试设置为T:

(setf *print-circle* t)
现在,您的回复应该很高兴:

CL-USER> (let ((foo (list 42)))
           (setf (rest foo) foo))
#1=(42 . #1#)

该符号允许读取器将表单的一部分影响到(读取器)变量,如
\1=…
,并在以后重用它,例如
\1
。这使得在读取或打印期间可以表示数据之间的循环交叉引用。在这里,我们可以看到变量
#1#
表示一个cons单元格,其中
CAR
是42,
CDR
#1#
本身。

答案很好!漂亮的回答!