Little Schemer:为什么要把(mk-length mk-length) 包装成一个函数?

Little Schemer: why wrap (mk-length mk-length) into a function?

The Little Schemer book, in Chapter 9, while building a length function for arbitrary long input, the following is suggested (on pages 170-171), that in the following code snippet (from page 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))))                   

部分 (mk-length mk-length) 永远不会 return 并且会无限地应用于自身:

Because we just keep applying mk-length to itself again and again and again...

But now that we have extracted (mk-length mk-length) from the function that makes length it does not return a function anymore.

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

Turn the application of mk-length to itself in our last correct version of length into a function.

喜欢,所以:

((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)

    does not return a function

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

    (lambda (x)
      ((mk-length mk-length) x))
    
  2. 如何将 (mk-length mk-length) 包装到一个函数中来解决 'never returning'(即无限递归)问题?我的理解是,在:

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

x 将被传递给无限递归函数,永远不会 returns。

这次通话

(mk-length mk-length)

只会调用 mk-length 一次。

如果mk-length恰好调用它自己,那么mk-length的主体将 再次被评估——但 mk-length 并不总是调用自己。

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

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

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

   ((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)

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

将其包装在 lambda(又名 eta-expansion)中会延迟应用程序 (3),直到在 [= 中调用构造的 length 18=],完全没问题(你复制的也有一些错别字):

   ((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) 只是 returns 立即绑定到 length 的 lambda 函数,并且 (lambda (l) ...) 很高兴返回,这样当 that (lambda (l) ...) 应用于某些列表,可能导致此length1(4)中被调用,只有 then lambda (3) 中的应用程序 (mk-length mk-length) 才会真正执行——最终给我们新的 (lambda (l) ...) 匿名函数,它将被应用到(cdr l) 那里。

1length(lambda (x) ((mk-length mk-length) x)) 这意味着 (length (cdr l))((mk-length mk-length) (cdr l)) 相同(mk-length 绑定到整个 lambda 表达式 (5)),最终 ((lambda (l) ...) (cdr l)).

妮娜