为什么这个函数在一个无限循环中 - 学习lisp
(编辑:我现在还不担心TCO呢)为什么这个函数在一个无限循环中 - 学习lisp
我是(终于开始学习)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
更改测试的顺序更容易。
然后你会注意到,由于某种原因,你只能将参数列表中的第一个元素变平。 (3 4)
((1 2) (3 4))
等?我们应该真的以某种方式结合将平台car
与平坦化cdr
的结果相结合。
如果扁平列表的结果是一个列表,那么我们将需要组合这两个结果列表。此外,由于我们将结合列表,如果我们遇到一个原子,我们将不得不生成一个列表,因为这个原子变平。
太棒了。 +1给我提示而不给我答案。我有一种感觉,我必须像树一样处理一个嵌套列表,并缓解每个分支。 – Ramy 2013-02-26 20:53:19
这就是(曾经是)称为“car-cdr”递归 - 最基本和最低效的。 :) – 2013-02-26 20:55:58
使用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
* (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] top
*
现在检查你的源代码。
NIL
由于实际和历史原因的混合,在Common Lisp中有点“奇怪”。例如:
-
NIL
是一个符号:(symbolp NIL) ==> T
-
NIL
是一个列表:(listp NIL) ==> T
-
NIL
是不一个cons单元:(consp NIL) ==> NIL
- ,但你可以利用它
car
/cdr
反正:(car NIL) ==> NIL
和(cdr NIL) ==> NIL
在您的代码中递归调用(car x)
时(listp (car x))
导致无限递归,因为NIL
是一个列表,其car
本身。
感谢我明确的原因,我是在一个无限循环。很有帮助。 – Ramy 2013-02-27 02:06:39
我会将变量'LST'称为'LIST'。我也会用'FIRST'和'REST'来代替'CAR'和'CDR'。 – 2013-02-26 21:03:11