首页 > 解决方案 > 计划中的按需调用

问题描述

我有这段代码知道参数是通过需要使用调用传递的:

(define fact-2
  (let ((foo (lambda (n f)
               (if (zero? n)
                   1
                   (f n f)))))
    (lambda (n)
      (let ((res 1))
        (foo n (begin 
                 (set! res (* res n)) 
                 (set! n (- n 1)) 
                 foo)) 
        res))))

我觉得我错过了一些东西,但是在需要调用foo这个对象 as的调用中f,它会计算f一次,然后永远不会更新resand n。这个对吗?我错过了什么吗?

谢谢你。

标签: parametersschemeparameter-passinganonymous-recursioncall-by-need

解决方案


你是对的。只有通过名称调用begin表达式才会在每次调用时被评估f(除了第一次)——以找出我们正在调用的内容,只有当调用f确实被调用时。

使用按值调用begin表达式将只被评估一次,在第一次调用之前foo

通过按需调用,它最多可能会被评估一次,何时以及是否在第一次调用结束时foo需要f再次调用。


让我们看看如何在常规的按值调用方案中模拟按名称调用的版本:

(define fact2
  (let ((foo (lambda (n f)
               (if (zero? (n))       ;; (n)   NB
                   1
                   ((f) n f)))))     ;; (f)   NB
    (lambda (n)
      (let ((res 1))
        (foo (lambda () n)           ;; (lambda () ...)    NB
             (lambda ()              ;; (lambda () ...)    NB
               (begin 
                 (set! res (* res n)) 
                 (set! n (- n 1)) 
                 foo))) 
        res))))

在 Racket 中调用(fact2 5)产生。120

对于按值调用的语义,您的代码不需要更改(在常规的按值调用方案中解释)并且当然会为(fact-2 5)调用循环(确实如此,在 Racket 中)。

并且在按需调用语义下,每个 lambda 的主体(两个新的 lambda 包装器)将仅在第一次调用时进行评估,然后它将保存计算的值然后返回它;并且对于所有后续调用,保存的值将立即返回,而不评估正文。因此,set!表单最多将被评估一次,并且带有示例测试调用的代码 (fact-2 5)将再次循环。


推荐阅读