Recursion 为什么这个函数在无限循环学习lisp中

Recursion 为什么这个函数在无限循环学习lisp中,recursion,lisp,infinite-loop,Recursion,Lisp,Infinite Loop,(编辑:我还不打算担心TCO) 我(终于开始)学习Lisp了。我正在尝试编写自己的(幼稚的)函数来压平列表。我从简单的案例开始,如果它不起作用,我将构建它来处理更复杂的案例。不幸的是,现在,我得到了一个无限循环,不太明白为什么 我也不知道如何在lisp中使用任何调试方法,所以如果你也能告诉我这个方向,我将不胜感激 (defun flattenizer (lst) (if (listp (car lst)) (flattenizer (car lst)) (if (null l

(编辑:我还不打算担心TCO)

我(终于开始)学习Lisp了。我正在尝试编写自己的(幼稚的)函数来压平列表。我从简单的案例开始,如果它不起作用,我将构建它来处理更复杂的案例。不幸的是,现在,我得到了一个无限循环,不太明白为什么

我也不知道如何在lisp中使用任何调试方法,所以如果你也能告诉我这个方向,我将不胜感激

(defun flattenizer (lst)
  (if (listp (car lst))
    (flattenizer (car lst))
    (if (null lst)
      nil
      (cons (car lst) (flattenizer (cdr lst))))))
最终代码:

(defun flattenizer (lst)
  (cond
    ((null lst) nil)
    ( (consp (car lst)) 
        (nconc (flattenizer (car lst)) (flattenizer (cdr lst)) ))
    (T (cons (car lst) (flattenizer (cdr lst))))))
测试:

* (flattenizer '((1 2) (3 4)))

(1 2 3 4)
* (flattenizer '(1 (2 3) (4 5)))

(1 2 3 4 5)
* (flattenizer '((1 2) 3 (4 5) 6))

(1 2 3 4 5 6)
* (flattenizer '(1 2 3 4))

(1 2 3 4)
(listp NIL)
返回
T
(listp(car NIL))
,因此当您到达列表末尾并在
NIL
上递归时,会发生循环

您需要更改测试的顺序,首先测试
(null lst)
,以避免循环。最好用
cond
重新编写。使用
cond
更容易更改测试顺序

然后你会注意到,出于某种原因,你只展平了参数列表中的第一个元素。在
((1 2)(3 4))
等中的
(3 4)
怎么样。?我们真的应该以某种方式将平整
汽车
的结果与平整
cdr
的结果结合起来


如果展平列表的结果是一个列表,那么我们需要合并两个结果列表。此外,由于我们将合并列表,如果遇到原子,我们将不得不生成一个列表,作为平坦化该原子的结果。

使用SBCL调试。

告诉SBCL您想要调试:

* (proclaim '(optimize (debug 3)))
您的代码:

* (defun flattenizer (lst)
    (if (listp (car lst))
      (flattenizer (car lst))
      (if (null lst)
        nil
        (cons (car lst) (flattenizer (cdr lst))))))

FLATTENIZER
使用
步骤单步执行

* (step (flattenizer '(1 (2 3) 4)))
; Evaluating call:
;   (FLATTENIZER '(1 (2 3) 4))
; With arguments:
;   (1 (2 3) 4)
下一步

1] step
; Evaluating call:
;   (FLATTENIZER (CDR LST))
; With arguments:
;   ((2 3) 4)
1] step
; Evaluating call:
;   (FLATTENIZER (CAR LST))
; With arguments:
;   (2 3)
1] step
; Evaluating call:
;   (FLATTENIZER (CDR LST))
; With arguments:
;   (3)
1] step
; Evaluating call:
;   (FLATTENIZER (CDR LST))
; With arguments:
;   NIL
1] step
; Evaluating call:
;   (FLATTENIZER (CAR LST))
; With arguments:
;   NIL
1] step
; Evaluating call:
;   (FLATTENIZER (CAR LST))
; With arguments:
;   NIL
下一步

1] step
; Evaluating call:
;   (FLATTENIZER (CDR LST))
; With arguments:
;   ((2 3) 4)
1] step
; Evaluating call:
;   (FLATTENIZER (CAR LST))
; With arguments:
;   (2 3)
1] step
; Evaluating call:
;   (FLATTENIZER (CDR LST))
; With arguments:
;   (3)
1] step
; Evaluating call:
;   (FLATTENIZER (CDR LST))
; With arguments:
;   NIL
1] step
; Evaluating call:
;   (FLATTENIZER (CAR LST))
; With arguments:
;   NIL
1] step
; Evaluating call:
;   (FLATTENIZER (CAR LST))
; With arguments:
;   NIL
下一步

1] step
; Evaluating call:
;   (FLATTENIZER (CDR LST))
; With arguments:
;   ((2 3) 4)
1] step
; Evaluating call:
;   (FLATTENIZER (CAR LST))
; With arguments:
;   (2 3)
1] step
; Evaluating call:
;   (FLATTENIZER (CDR LST))
; With arguments:
;   (3)
1] step
; Evaluating call:
;   (FLATTENIZER (CDR LST))
; With arguments:
;   NIL
1] step
; Evaluating call:
;   (FLATTENIZER (CAR LST))
; With arguments:
;   NIL
1] step
; Evaluating call:
;   (FLATTENIZER (CAR LST))
; With arguments:
;   NIL
下一步

1] step
; Evaluating call:
;   (FLATTENIZER (CDR LST))
; With arguments:
;   ((2 3) 4)
1] step
; Evaluating call:
;   (FLATTENIZER (CAR LST))
; With arguments:
;   (2 3)
1] step
; Evaluating call:
;   (FLATTENIZER (CDR LST))
; With arguments:
;   (3)
1] step
; Evaluating call:
;   (FLATTENIZER (CDR LST))
; With arguments:
;   NIL
1] step
; Evaluating call:
;   (FLATTENIZER (CAR LST))
; With arguments:
;   NIL
1] step
; Evaluating call:
;   (FLATTENIZER (CAR LST))
; With arguments:
;   NIL
下一步

1] step
; Evaluating call:
;   (FLATTENIZER (CDR LST))
; With arguments:
;   ((2 3) 4)
1] step
; Evaluating call:
;   (FLATTENIZER (CAR LST))
; With arguments:
;   (2 3)
1] step
; Evaluating call:
;   (FLATTENIZER (CDR LST))
; With arguments:
;   (3)
1] step
; Evaluating call:
;   (FLATTENIZER (CDR LST))
; With arguments:
;   NIL
1] step
; Evaluating call:
;   (FLATTENIZER (CAR LST))
; With arguments:
;   NIL
1] step
; Evaluating call:
;   (FLATTENIZER (CAR LST))
; With arguments:
;   NIL
下一步

1] step
; Evaluating call:
;   (FLATTENIZER (CDR LST))
; With arguments:
;   ((2 3) 4)
1] step
; Evaluating call:
;   (FLATTENIZER (CAR LST))
; With arguments:
;   (2 3)
1] step
; Evaluating call:
;   (FLATTENIZER (CDR LST))
; With arguments:
;   (3)
1] step
; Evaluating call:
;   (FLATTENIZER (CDR LST))
; With arguments:
;   NIL
1] step
; Evaluating call:
;   (FLATTENIZER (CAR LST))
; With arguments:
;   NIL
1] step
; Evaluating call:
;   (FLATTENIZER (CAR LST))
; With arguments:
;   NIL
看来你现在陷入了困境

回到顶层

1] top

*

现在检查您的源代码。

NIL
在Common Lisp中有点“奇怪”,因为它既有实际原因,也有历史原因。例如:

  • NIL
    是一个符号:
    (symbolp NIL)==>T
  • NIL
    是一个列表:
    (listp NIL)==>T
  • NIL
    不是cons单元格:
    (consp NIL)==>NIL
  • 但是你可以把它当作
    car
    /
    cdr
    (carnil)==>NIL
    (cdrnil)==>NIL

在您的代码中,当
(listp(car x))
导致无限递归时,递归调用
(car x)
,因为
NIL
是一个列表,而它的
car
本身就是它。

+谢谢你给了我提示而没有给我答案。我有一种感觉,我必须像对待树一样对待嵌套列表,并沿着每个分支递归。这就是(曾经)所谓的“car-cdr”递归——最基本、最低效的递归我会调用变量
LST
只是
LIST
。我也会先使用
FIRST
REST
,而不是
CAR
CDR
。感谢我处于无限循环中的明确原因。非常有用。