2

The Little Schemer book的第 9 章中,在为任意长输入构建length函数时,建议如下(在第 170-171 页),在以下代码片段中(来自第 168 页本身):

((lambda (mk-length)
   (mk-length mk-length))                      
 (lambda (mk-length)
   ((lambda (length)                           
      (lambda (l)
        (cond
          ((null? l) 0)
          (else (add1 (length (cdr l)))))))   
    (mk-length mk-length))))                   

part (mk-length mk-length), 将永远不会返回,并将无限地应用于自身:

因为我们只是mk-length一次又一次地向自己申请......

但是现在我们已经(mk-length mk-length)从使它length不再返回函数的函数中提取出来了。

现在,为了解决这个问题,本书建议:

mk-length我们最后一个正确版本 of 中 to 自身的应用length变成一个函数。

就像,所以:

((lambda (mk-length)
   (mk-length mk-length))
 (lambda (mk-length)
   ((lambda (length)
     (lambda (l)
       (cond
         ((null? l) 0 )
         (else 
           (add1 (length (cdr l)))))))
   (lambda (x)
     ((mk-length mk-length) x)))))

我感到困惑的是:

  1. 如果(mk-length mk-length)

    不返回函数

    我们如何将结果应用(mk-length mk-length)到某个东西上,就好像它是一个函数一样?

    (lambda (x)
      ((mk-length mk-length) x))
    
  2. 包装(mk-length mk-length)成函数如何解决“永不返回”(即无限递归)问题?我的理解是,在:

    (lambda (x)
      ((mk-length mk-length) x))
    

x只会被传递给永远不会返回的无限递归函数。

4

2 回答 2

3

这个电话

(mk-length mk-length)

只会调用mk-length一次。

如果mk-length碰巧称其为自身,那么mk-length将再次评估的主体——但mk-length并不总是称其为自身。

至于为什么——请注意,您的表达式中没有任何函数使用define. lambda所有引入匿名函数的函数表达式都使用。

该示例表明,即使只使用匿名函数,也可以编写递归函数。不是直接命名函数(使用define),而是将函数作为参数传递给另一个函数——并且该函数为其参数命名。

于 2017-12-29T12:11:55.117 回答
3

您可能复制了错误的代码片段,即您实际谈论的代码片段之前的代码片段。您显示的第一个代码完全没问题。更确切地说,循环是这个:

   ((lambda (mk-length)
      (mk-length mk-length))                      ; (1)
    (lambda (mk-length)
      ((lambda (length)                           ; (2)
         (lambda (l)
           (cond
             ((null? l) 0)
             (else (add1 (length (cdr l)))))))    ; (4)
       (mk-length mk-length))))                   ; (3)

这里已经回答:应用程序(1)触发应用程序立即(2)触发应用程序,相当于!因此,循环。(3) (1)

将其包装在 lambda (又名eta-expansion)中会延迟应用程序(3),直到调用构造函数lengthin (4),这完全没问题(您也复制了一些错别字):

   ((lambda (mk-length)
      (mk-length mk-length))                      ; (1)
    (lambda (mk-length)                                   ; (5)
      ((lambda (length)                           ; (2)
         (lambda (l)
           (cond
             ((null? l) 0)
             (else (add1 (length (cdr l)))))))    ; (4)
       (lambda (x)                                ; (3)
         (mk-length mk-length) x))))

(3)现在是 lambda 表达式,而不是应用程序。计算这个 lambda 表达式会产生一个匿名函数。这个 lambda 函数将在(mk-length mk-length) 稍后被调用时执行应用程序length

(更长的解释:) (3)只是立即返回绑定到 的 lambda 函数length,并且(lambda (l) ...)很高兴地返回,这样当它将 应用于某个列表时,可能导致 this (lambda (l) ...) 1length调用(4),只有这样(mk-length mk-length)lambda 内的应用程序(3)才会实际执行——(lambda (l) ...)最终给我们新的匿名函数,它将被应用到(cdr l)那里。

1意味着它length与(绑定到整个 lambda-expression )相同,最终,.(lambda (x) ((mk-length mk-length) x))(length (cdr l))((mk-length mk-length) (cdr l))mk-length(5)((lambda (l) ...) (cdr l))

尼娜

于 2017-12-29T16:55:47.587 回答