【问题标题】:Little Schemer: why wrap (mk-length mk-length) into a function?Little Schemer:为什么将 (mk-length mk-length) 包装成一个函数?
【发布时间】:2018-06-09 15:52:08
【问题描述】:

在第 9 章的The Little Schemer book 中,在为任意长输入构建length 函数时,建议如下(在pages 170-171 上),在以下代码中 sn-p(来自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) 部分将永远不会返回,并将无限地应用于自身:

因为我们只是一次又一次地将mk-length 应用到自身...

但是现在我们已经从生成length 的函数中提取了(mk-length mk-length),它不再返回一个函数。

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

在我们最后一个正确版本的length 中将mk-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 只会被传递给无限递归函数,它永远不会返回。

【问题讨论】:

  • 不是无限递归。每个步骤都使用(cdr l) 作为参数,当它变为空时停止。
  • 为什么你认为(mk-length mk-length) 不返回函数? mk-length 的值是第二个 lambda 表达式,它返回 (lambda (l) ...),这是一个函数。
  • 参见。 Y combinator discussion in “The Little Schemer” 讨论了同一段代码。你的问题是already answered那里。
  • @DanD。虽然在那一点上仍然不是组合器,只是越来越接近它。当(lambda (l) ...) 被抽象掉时,它就变成了组合子。

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


【解决方案1】:

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

   ((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)触发应用程序(3)马上,相当于(1)!因此,循环。

将其包装在 lambda(又名 eta-expansion)中会延迟应用程序 (3),直到在 (4) 中调用构造的 length,这完全可以(您复制了这也有一些错别字):

   ((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 函数在调用 length 时执行应用程序 (mk-length mk-length)稍后

(更长的解释:) (3) 只是立即返回绑定到 length 的 lambda 函数,并且 (lambda (l) ...) 很高兴地返回,这样当 that (lambda (l) ...) 应用于一些列表,可能会导致 (4) 中调用此 length1,只有 然后 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)).

尼娜

【讨论】:

  • 感谢您的详细解释并指出我的拼写错误和“错误应对”(我相信我现在已经更正了)。
【解决方案2】:

这个电话

(mk-length mk-length)

只会调用一次mk-length

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

至于原因——请注意,您的表达式中没有使用define 命名的函数。所有函数表达式都使用lambda,它引入了一个匿名函数。

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

【讨论】:

    猜你喜欢
    • 2013-11-12
    • 2015-10-22
    • 2016-11-15
    • 1970-01-01
    • 2012-04-11
    • 1970-01-01
    • 2010-12-15
    • 1970-01-01
    • 2012-04-26
    相关资源
    最近更新 更多