我正在阅读 The Little Schemer ,并对以下代码感到困惑:
((lambda (len)
(lambda (l)
(cond
((null? l) 0)
(else
(+ 1 (len (cdr l)))))))
eternity)
(define eternity
(lambda (x)
(eternity x)))
代码是确定空列表,否则它永远不会停止。
为什么“len
”没有递归?
答案 0 :(得分:5)
虽然将文本替换应用于Lisp表单可能很危险(因为存在多重评估的危险等),在这种情况下,查看此表单并查看它如何组合在一起可能会有所帮助:
((lambda (len)
(lambda (l)
...))
eternity)
是一个应用程序,即函数调用。被调用的函数接受一个名为len
的参数,并返回另一个带有单个参数l
的函数。调用函数使用eternity
调用。调用完成后,结果就是这个函数:
(lambda (l)
(cond
((null? l) 0)
(else (+ 1 (eternity (cdr l))))))
现在,此函数采用列表l
,如果它为空,则返回0
。否则,它会计算(cdr l)
(列表的其余部分),并使用该值调用eternity
。当返回时,1
被添加到结果中,这是整个函数的返回值。问题当然是eternity
(define eternity
(lambda (x)
(eternity x)))
也可以写成
(define (eternity x)
(eternity x))
只需使用参数x
,然后使用eternity
调用x
。这是一个无限循环。在上面,我写了“当它返回时”,但事实上,(eternity (cdr l))
从不返回。所以,
((lambda (len)
(lambda (l)
(cond
((null? l) 0)
(else (+ 1 (len (cdr l)))))))
eternity)
是一个函数调用,它返回一个函数(lambda (l) …)
,如果使用空列表调用则返回0
,并进入一个带有非空列表的无限循环。
从程序分析方面来看,值得注意的是是其他值,这些值不会进入无限循环。例如,如果您使用字符串调用它,那么(cdr l)
将是一个错误。
答案 1 :(得分:2)
就像你说的,这是一个长度函数定义为部分函数,它只对空列表完成。但是这还没有进入y-combinator部分,它不是一个匿名函数调用自身的例子。
l
是一个原子列表,它是评估(lambda (len) ...)
时返回的函数的参数。
len
是作为参数传递到外部lambda的函数。
外部表达式创建一个传入eternity
的lambda作为其参数。外部lambda返回通过计算内部lambda创建的函数,返回的函数是以eternity
作为参数的东西。
如果代码传递了一个空列表(意味着在另一组parens中包装整个第一部分后跟'()
),那么它当然会评估为0。 len
永远不会被评估。
如果代码被传递给非空的lat,那么它将尝试评估len
参数并获得无限递归。