【问题标题】:Scheme: Compiling with continuations方案:继续编译
【发布时间】:2020-09-23 11:59:36
【问题描述】:

我目前在 OCaml 中为方案的子集编写编译器,并且无法理解如何使用延续进行编译。我找到了一些很棒的资源,即:

使用 anormal-paper 中介绍的异常转换,我现在有代码,其中函数调用要么绑定到变量,要么返回。

例子:

(define (fib n)
  (if (<= n 1)
      n
      (+ (fib (- n 1)) 
         (fib (- n 2)))))

变成:

(define (fib n)
  (let ([c (<= n 1)])
    (if c
        n
        (let ([n-1 (- n 1)])
          (let ([v0 (fib n-1)])
             (let ([n-2 (- n 2)])
               (let ([v1 (fib n-2)])
                 (+ v0 v1)))))))

为了进行 cps-transform,我现在必须:

  1. 为所有非原始函数添加 cont 参数
  2. 在尾部位置调用 cont 参数
  3. 转换所有非原始函数调用,使它们逃脱 let-binding 并成为一个额外的 lambda,之前的 le​​t-bound 变量作为唯一参数,之前的 le​​t-body 作为身体

结果如下:

(define (fib n k)
  (let ([c (<= n 1)])
    (if c
        (k n)
        (let ([n-1 (- n 1)])
          (fib n-1 
            (lambda (v0) 
              (let ([n-2 (- n 2)]) 
                (fib n-2
                  (lambda (v1) 
                    (k (+ v0 v1))))))))))

这对吗?

csmu 课程还讨论了 CPS 中的程序如何不需要堆栈并且永不返回。那是因为我们不需要保存要返回的地址,而闭包以及其他数据类型都存储在堆上,并且使用闭包使引用保持活动状态?

csmu 还谈到了 call/cc 的脱糖:

(call/cc) => ((lambda (k f) (f k k)))

当使用这种脱糖时,如何:

(+ 2 (call/cc (lambda (k) (k 2))))

在 MIT-Scheme 中返回 4,因为当前的延续可能类似于显示?

【问题讨论】:

    标签: functional-programming compiler-construction scheme


    【解决方案1】:

    这对吗?

    (define (fib n k)
      (let ([c (<= n 1)])
        (if c
            (k n)
            (let ([n-1 (- n 1)])
              (fib n-1 
                (lambda (v0) 
                  (let ([n-2 (- n 2)]) 
                    (fib n-2
                      (lambda (v1) 
                        (k (+ v0 v1))))))))))
    

    你得到一个 A+ ?


    csmu 课程还讨论了 CPS 中的程序如何不需要堆栈并且永不返回。是不是因为我们不需要保存要返回的地址,而闭包以及其他数据类型都存储在堆上,并且通过使用闭包来保持引用保持活动状态?

    没错!请参阅 Chicken Complilation Process 以深入了解这种技术。


    csmu 还谈到了 call/cc 的脱糖:

    (call/cc) => ((lambda (k f) (f k k)))
    

    看起来不太对劲。这是来自Matt Mightcall/cc 的脱糖-

    call/cc => (lambda (f cc) (f (lambda (x k) (cc x)) cc))
    

    【讨论】:

      【解决方案2】:

      使用延续编译的想法的本质是,您希望对传递给每个函数的参数的求值进行排序,并在评估该参数后将其值发送给传递的延续。

      你重写CPS形式代码的语言要求是尾递归的,否则它会堆叠空帧,后面只有一个return。如果实现语言不强制执行尾递归,则需要应用更复杂的方法来为 cps 代码获取非增长堆栈。

      小心,如果你这样做了,你还需要更改原语的签名。原语也将传递一个延续,但它们会立即在传递的延续中返回答案,它们不会创建其他延续。

      了解如何使用延续进行编译的最佳参考仍然是book of Andrew W. Appel,您不需要更多。

      【讨论】:

      • CPS 没有堆栈,因为函数调用变成了 goto。 researchgate.net/publication/…
      • @ceving 完全正确。您应该阅读您引用的文章以了解如何在不堆叠框架的情况下实现它。
      猜你喜欢
      • 2019-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多