`
daweibalong
  • 浏览: 45328 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
社区版块
存档分类
最新评论

Continuation与CPS(Continuation Passing Style)的理解

阅读更多

      Scheme是最早支持Continuation的语言,而Continuation对于初学者来说还是比较难以理解的,以下是我在学习TSPL中Continuation和CPS相关章节时的一些理解。更多Scheme、Continuation、CPS的相关知识到http://www.scheme.com/tspl4/ 查找学习。

* Continuation

一个表达式的continuation就是外部函数要利用该表达式产生的结果做什么。 比如:
(cons (exp) 'a)
这里面(exp)的continuation就是等待exp的结果然后将其cons到'a上,它保存 了等待(exp)结果的计算点并进行后续计算的函数调用栈! 在scheme中,所有的continuation都可以用call/cc(call with current continuation来捕捉到(call/cc必须接受一个单一参数的函数作为参数):
(call/cc
  (lambda (k)
     exp))
这里面k就是捕捉到的continuation。 每当显式的调用k的时候,我们就可以简单的把它想成直接将 (call/cc…)替换成传递给k的值并继续执行。比如call/cc放在递归的终止条件处,那么 每次调用continuation的时候就会将终止返回值替换成传递给coninuation的 值,再或者局部退出的例子(下面程序),将call/cc放到内部函数的最外部,调用 continuation的时候就直接结束程序了。 下面是利用call/cc来写的product函数:
(define product
  (lambda (ls)
    ;显式调用break的时候,直接跳到这里,也就是返回(call/cc。。。)的返
  回值
    (call/cc  
      (lambda (break)
        (let f ([ls ls])
          (cond
            [(null? ls) 1]
            [(= (car ls) 0) (break "zero")]
            [else (* (car ls) (f (cdr ls)))]))))))
;test
(product '(1 2 3 4 5))  => 120
(product '(7 3 8 0 1 9 5)) =>  "zero"
关于那个阴阳谜题,如果用上面这种代换的方式来考虑的话,似乎就没有那么难理解 了。

* CPS

  1. 正常函数的返回都隐含一个continuation,就是利用这个函数的返回值来 做的后续事情,而cps的本质就是将这个隐式的continuation显式的当做参 数传递进去,并在函数中完成应有的continuation并将最终结果返回。
  2. 这跟尾递归似乎很像,在改造递归为尾递归的时候,就将当前状态通过accumulator汇集到函数内部的操作,当达到结束条件时返回汇集结果,而不必再返回来收集递归过程中的返回值。
  3. cps似乎就是同样的道理,每次将continuation传递到内部进行操作的组合,当达到底部的时候直接将汇集的continuation的计算结果返回,而不必返回来再去计算每一步的continuation。

** TSPL的练习 3.4.3

Rewrite the following expression in CPS to avoid using call/cc.
(define reciprocals
  (lambda (ls)
    (call/cc
      (lambda (k)
        (map (lambda (x)
               (if (= x 0)
                   (k "zero found")
                   (/ 1 x)))
             ls)))))
(reciprocals '(2 1/3 5 1/4))  (1/2 3 1/5 4)
(reciprocals '(2 1/3 0 5 1/4))  "zero found"
这道题是要用CPS的形式重写上面的函数,就是实现局部退出的功能。如果不规 定用map函数来写的话,我们可以按照之前product的CPS形式的写法来写
(define reciprocals
    (lambda (ls k)
      (let ([break k])
        (let f ([ls ls] [k k])
          (cond
            [(null? ls) (k '())]
            [(= (car ls) 0) (break "zero!")]
            [else (f (cdr ls)
                     (lambda (x)
                       (k (cons 
                            (/ 1 (car ls))
                            x))))])))))
但如果不改变原来实现的方式,也就是用map函数和CPS来实现的话就会相对麻烦, 如果对CPS和Continuation理解不透彻的话会非常别扭,而且难以理解。Kent在 书中也给了提示:让我们看一下map1的实现,也就是说我们要实现一个可以传递 continuation的map,然后在转换函数中选择不同的continuation来完成异常情 况的检查与退出。 先看一下map1的实现:
(define map1
  (lambda (p ls)
    (if (null? ls)
        '()
        (cons (p (car ls))
              (map1 p (cdr ls))))))
在这里让人比较迷惑的是在非尾部调用的过程中,cons会等待p 和 map1两个函数的结果 来进行后续的计算,也就是存在两个延续,那要怎么安排才好呢?我们看一下 tspl中的原话:
When one procedure invokes another via a nontail call, the called procedure receives an implicit continuation that is responsible for completing what is left of the calling procedure's body plus returning to the calling procedure's continuation. If the call is a tail call, the called procedure simply receives the continuation of the calling procedure.
也就是说,非尾部调用的时候,被调用函数会隐式的接收到一个continuation, 这个continuation代表着"完成调用函数其他部分加上被调用函数的返回值". 如 果我们按照从右往左的计算顺序,那么我们可以对map1进行第一步改造:将隐式传递 给内部map1的continuation显式的写出来:
(define map1
  (lambda (p ls k)
    (if (null? ls)
        (k '())
        (map1 p 
              (cdr ls)
              (lambda (v)
                (cons (p (car ls)) v))))))
然后我们看到函数p同样是一个非尾部调用,那我们进一步将传递给p的隐式的 continuation显式的写出来:
(cons (p (car ls)) v)
=> (p (car ls) 
      (lambda (n)
         (k (cons n v))))
所以可以得到map1的CPS版本:
(define map1
  (lambda (p ls k)
    (if (null? ls)
        (k '())
        (map1 p 
              (cdr ls)
              (lambda (v)
                (p (car ls) 
                   (lambda (n)
                     (k (cons n v)))))))))
在这里,函数p接受continuation,这个continuation是执行将p的结果cons到 map1的结果中,而在p函数中会根据是否产生异常来决定是使用这个 continuation还是退出的continuation。又因为map1在 reciprocals 中是一个 尾部调用,所以直接传递延续即可,所以reciprocals的任务就是向map1中 传递正确的p函数、延续以及退出的延续(就是map1开始前的延续):
(define reciprocals
  (lambda (ls k)
    (let ([break k])
      (map1
       (lambda (x k)
         (if (= x 0)
             (break "error")
             (k (/ 1 x))))
       ls
       k))))
测试结果:
(reciprocals '(2 1/3 5 1/4) (lambda (x) x)) =>  (1/2 3 1/5 4)
(reciprocals '(2 1/3 0 5 1/4) (lambda (x) x)) => "error"
最后简化一下,把map1写成内部定义的形式:
(define reciprocals
    (lambda (ls k)
      (let ([break k])
        (let map1 ([p (lambda (x k)
                        (if (= x 0)
                            (break "error")
                            (k (/ 1 x))))]
                   [ls ls]
                   [k k])
          (if (null? ls)
              (k '())
              (map1
                p
                (cdr ls)
                (lambda (v)
                  (p (car ls) 
                     (lambda (n)
                       (k (cons n v)))))))))))
 
 
0
0
分享到:
评论

相关推荐

    JooseX-CPS:Joose中“ Continuation Passing Style”的一些语法糖

    JooseX.CPS-JavaScript的Continuation Passing Style 实现以及一些语法糖,简化了其在Joose方法和方法修饰符中的使用 概要 独立使用: UI.maskScreen("Please wait") TRY(function (url, data) { XHR....

    CPS部分代码 - 5

    在IT行业中,CPS(Continuation Passing Style,延续传递风格)是一种编程范式,它将控制流通过函数调用的参数传递,而不是使用传统的返回机制。这种编程风格常见于函数式编程语言,但也可用于命令式语言。在这个...

    CPS部分代码 - 3

    CPS,全称为Continuation Passing Style(延续传递风格),是一种编程范式,它将控制流程的传递通过函数调用的参数来实现,而非传统的跳转指令。这种编程风格在函数式编程、编译器设计、异步编程等领域有着广泛的...

    CPS部分代码 -2.22

    CPS,全称为Continuation Passing Style(延续传递风格),是一种编程范式,它将控制流程转化为函数调用的形式,使得程序的控制权通过函数参数的形式传递。在CPS中,函数不再直接返回结果,而是接受一个继续执行的...

    CPS -- 3.6

    CPS,全称为Continuation Passing Style(延续传递风格),是一种编程范式,它将控制流程转化为函数调用的形式,使得程序的控制权通过函数参数在各个函数之间传递。在CPS中,函数不再直接返回结果,而是接受一个代表...

    CPS部分代码 - 2.23

    CPS,全称为Continuation Passing Style(延续传递风格),是一种编程范式,它将函数调用的返回操作作为参数传递给另一个函数,而不是直接返回结果。这种编程风格在函数式编程语言中尤其常见,但也可以在命令式语言...

    CPS部分代码

    CPS,全称为Continuation Passing Style(延续传递风格),是一种编程范式,它将函数调用的返回过程作为参数传递给另一个函数,而不是直接返回结果。这种编程方式在函数式编程语言中尤其常见,但也可以在命令式语言...

    CPS部分代码 - 2.24

    CPS,全称为Continuation Passing Style(延续传递风格),是一种编程范式,它将函数调用的返回操作作为参数传递给另一个函数,而不是直接返回结果。这种编程风格在某些领域,如编译器构造、异步编程和控制流管理等...

    cps.zip_40_CPS_site:www.pudn.com_王垠40行代码

    标题中的“cps.zip_40_CPS_site:www.pudn.com_王垠40行代码”表明这是一个关于计算机编程的压缩文件,其中包含了王垠编写的40行代码,用于实现CPS(Continuation-Passing Style)转换。CPS是一种编程范式,常在函数...

    CPS部分代码 - 6

    CPS,全称为Continuation Passing Style(延续传递风格),是一种编程范式,它将函数调用的返回操作作为参数传递给另一个函数,而不是直接返回结果。这种编程模式在处理异步操作、事件驱动编程以及优化编译器时非常...

    CPS部分代码 - 4

    CPS,全称为Continuation Passing Style(延续传递风格),是一种编程范式,它将函数调用的返回过程转化为传递一个函数作为参数。这种方式在处理异步操作、事件驱动编程、编译器构造等领域中有着广泛的应用。在这个...

    C#中的递归APS和CPS模式详解

    在C#编程中,递归APS(Accumulator Passing Style)和CPS(Continuation Passing Style)模式是两种处理递归和控制流程的不同方法。理解并掌握这两种模式可以帮助开发者编写更高效、更灵活的代码。 首先,让我们...

    dotty-cps-async:Dotty的实验性CPS变压器

    “dotty-cps-async”是一个与Dotty编程语言相关的实验项目,主要涉及控制流转换(Continuation Passing Style, CPS)和异步编程。Dotty是Scala的未来版本,它旨在改进语言设计,提升编译器性能,并提供更现代的类型...

    nodejs中文学习手册

    2.4 CPS(Continuation-Passing Style) . . . . . . . . . . . . . . . . 16 2.5 函數返回函數與 Currying . . . . . . . . . . . . . . . . . . . . . 17 2.6 流程控制 . . . . . . . . . . . . . . . . . . . ....

    cps:尝试实现cps转换和协程

    在编程领域,`CPS`(Continuation Passing Style,延续传递风格)是一种编程范式,它将函数调用的控制流转化为参数传递,常用于异步操作和错误处理。CPS通过将函数的返回值作为参数传递给另一个函数(即“延续”),...

    Compiling with Continuations

    本书深入探讨了延续传递风格(Continuation-passing style, 简称CPS)在编译过程中的应用及其相关优化技术。CPS是一种编程范式,它涉及将函数的控制权显式地转换为一个“延续”参数,允许编译器在不同的上下文中更...

    groovy-cps:连续传递样式中的Groovy执行

    "groovy-cps"是Groovy的一个重要概念,它涉及到Groovy代码的连续传递样式(Continuation Passing Style,CPS)执行。 连续传递样式是一种编程范式,它将函数调用的返回值作为参数传递给另一个函数,而不是直接返回...

    cps:Nim的连续传递样式:link:

    在Nim编程语言中,CPS(Continuation-Passing Style,连续传递样式)是一种编程范式,它将控制流程转化为函数参数的传递。这种风格在处理异步操作、协程、并发和并行计算时非常有用。让我们深入探讨Nim中的CPS以及它...

    Python库 | stacklesslib-1.2.2-py2.7.egg

    6. **CPS转换(Continuation-Passing Style)**: Stackless Python支持将函数转换为Continuation-Passing Style,这种编程风格有利于处理异步操作和事件驱动编程。 7. **代码可读性**: 尽管增加了这些高级功能,...

    The Little Schemer

    Continuation Passing Style (CPS)是一种编程范式,它将控制流转化为函数调用,有助于理解和实现异步编程和中间状态的管理。理解这一概念对于深入LISP和函数式编程至关重要。 Y Combinator是λ演算中的一个重要概念...

Global site tag (gtag.js) - Google Analytics