匿名函数在Scheme中调用自身的机制?

时间:2013-10-23 14:31:19

标签: recursion scheme the-little-schemer anonymous-recursion

我正在阅读 The Little Schemer ,并对以下代码感到困惑:

((lambda (len)
   (lambda (l)
      (cond
         ((null? l) 0)
         (else 
            (+ 1 (len (cdr l)))))))
   eternity)

(define eternity
    (lambda (x)
         (eternity x)))

代码是确定空列表,否则它永远不会停止。

为什么“len”没有递归?

2 个答案:

答案 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参数并获得无限递归。