scheme Continuation
Continuation Pass Style
在函数式编程(FP)中有一种被称为Continuation Passing Style(CPS)的风格。在这种风格的背后所蕴含的思想就是将处理中可变的一部分抽象为一个function,并将其作为一个参数传入。这是高度抽象的方法,所带来的表达的威力也是无与伦比的。
下面举一个例子,实现CPS描述的fold函数。
在此之前需要简单介绍一下fold概念,在FP中这是一个非常有效的处理list的工具。一般它的signature是fold(f,initial,list)。它所对应的效果是:f(e0,f(e1,….f(en,initial)…),e0到en也就是list中的所有元素,当我们取f为加法操作的时候就可以计算得到所有元素的和。(Note:这里我提到的fold是右结合的,考虑到有时候f没有结合律,所以事实上还存在一个左结合的fold)
下面正式引入CPS风格的fold:
cfold(f,z,list) = f(e0,z, lambda y.cfold(f,y,er))
其中的e0为list第一个元素,er为除去了e0以外元素构成的list,lambda y:cfold(f,y,er)表示一个匿名的函数,它接受一个参数,递归的执行cfold。整个函数的终止条件是list为空的时候。
cfold和fold不同的地方就在于第一个参数f上,cfold的f接受三个参数,这三个参数的意义可以从cfold的定义式中推敲。
看上去cfold比fold复杂了不少,但是由此其表达能力有了进一步的提升,下面两个即由cfold定义的fold:
fold1(f,z,list) = cfold( lambda x g t.f(x,t(g)), z, list )
fold2(f,z,list) = cfold( lambda x g t.t(f(x,g)), z, list )
fold1的功能和fold完全相同,fold2则可以逆序的执行运算f(en,f(e[n-1],….f(e0,initial)…)
这就是CPS的强大之处,事实上这也正是数学中函数的强大表达能力的体现。
Reference : Yet Another Haskell Tutorial Section 4.6
Continuation is the calculation what should be performed before returning to the toplevel. Actually, the continuation exists everywhere during computation. For instance, in the case of (* 3 (+ 1 2)),
the calculation that should be done is multiplying 3 { (* 3 []) }
after evaluating (+ 1 2). As most of languages do not treat it explicitly, however, it is unfamiliar ot many programmers.
3. Continuation Passing Style (CPS)
3.1. Simple CPS
CPS is a programming style in which the successive function(s) that uses the result of current function is given as an argument of the current function. [code 1] shows adding and multiplying written in CPS. In k+ and k*, k is the successive function.
[code 1]
(define (return x)
x) (define (k+ a b k)
(k (+ a b))) (define (k* a b k)
(k (* a b)))
[example 1] shows how to calculate (* 3 (+ 1 2)) using the CPS.
[example 1]
(k+ 1 2 (lambda (x) (k* x 3 return)))
In the ordinary form of Scheme, values that are calculated in parentheses go outside of them. In the CPS, on the contrary, values go inside of other parentheses. In [example 1], k+ passes the value of (+ 1 2) to (lambda (x) (k* x 3 return)) and k* passes the result of (* (+ 1 2) 3) to return.
在一般scheme代码中,在圆括号内被计算的值到外面使用,在cps恰好相反,值又到了另一个圆括号内。k+函数传递(+ 1 2)到lambda函数中。
例子;
无限循环:
(let ((start #f))
(if (not start)
(call/cc (lambda (cc)
(set! start cc))) empty) (display )
(display )
(start)) ;start现在是一个存储过程
12121212........
Continuation(First-class continuation)是控制指令执行顺序的一种能力。可以用来从当前执行函数跳转回上层调用函数,或者跳转到之前以退出的函数。可以把它想象成将程序的当前状态保存下来,以便以后恢复(有点像给VM做snapshot)。但要注意的是,真正的Continuation仅仅存储执行上下文,而非程序的数据。下面是的比喻能够很好的解释这一点:
话说你在厨房的冰箱前,考虑来点三明治尝尝。如果这时候你做了个continuation,并将其存放到你的口袋中。然后你把火鸡和面包从冰箱里取出来拿到餐台,并把他们做成了你想要的三明治. 此时,如果你把你的continuation从口袋中取出来,并且调用一次的话。你会发现你突然又身处冰箱前, 考虑来点三明治尝尝. 不过,幸运的是, 此时餐台上已经有了个你想要的三明治。而火鸡和面包都不见了. 那么,你就可以径直去把它吃掉了。 :-) (有点像月光宝盒吧?)
Scheme 通过"catch" 以及 call/cc支持Continuation。Common Lisp虽然在标准中没有支持Continuation,但是通过cl-cont可以使用Delimited Continuation。(支持closure以及proper tail recursion的语言都可以实现continuation-passing style,从而实现Continuation。)
Continuation可以用来实现一些常用的设计模式,比如Coroutines (又叫做Green Thread),Exception handling等。以下给出用Scheme编写的演示代码:
(define the-continuation #f) (define (test)
(let ((i ))
; call/cc calls its first function argument, passing
; a continuation variable representing this point in
; the program as the argument to that function.
;
; In this case, the function argument assigns that
; continuation to the variable the-continuation.
;
(call/cc (lambda (k) (set! the-continuation k)))
;
; The next time the-continuation is called, we start here.
(set! i (+ i ))
i))
Defines a function test
that sets the-continuation
to the future execution state of itself:设置the-contiutaiton为未来的执行状态。
> (test)
1
> (the-continuation)
2
> (the-continuation)
3
> ; stores the current continuation (which will print 4 next) away
> (define another-continuation the-continuation)
> (test) ; resets the-continuation
1
> (the-continuation)
2
> (another-continuation) ; uses the previously stored continuation
4
For a gentler introduction to this mechanism, see call-with-current-continuation.
协程:
Coroutines
This example shows a possible usage of continuations to implement coroutines as separate threads
#lang racket
;;; A naive queue for thread scheduling.
;;; It holds a list of continuations "waiting to run". (define *queue* '()) (define (empty-queue?)
(null? *queue*)) (define (enqueue x)
(set! *queue* (append *queue* (list x)))) (define (dequeue)
(let ((x (car *queue*)))
(set! *queue* (cdr *queue*))
x)) ;;; This starts a new thread running (proc). (define (fork proc)
(call/cc
(lambda (k)
(enqueue k)
(proc)))) ;;; This yields the processor to another thread, if there is one. (define (yield)
(call/cc
(lambda (k)
(enqueue k)
((dequeue))))) ;;; This terminates the current thread, or the entire program
;;; if there are no other threads left. (define (thread-exit)
(if (empty-queue?)
(exit)
((dequeue)))) ;;; The body of some typical Scheme thread that does stuff:
(define (do-stuff-n-print str)
(lambda ()
(let loop ((n ))
(display str )(display n) (newline) (yield) ;每输出一次str,就将运行权交给其他线程
(loop (+ n))))) ;;; 创建并运行两个线程.
(fork (do-stuff-n-print "This is AAA"))
(fork (do-stuff-n-print "Hello from BBB"))
(thread-exit)
以下是程序执行结果:
This is AAA 0
Hello from BBB 0
This is AAA 1
Hello from BBB 1
This is AAA 2
Hello from BBB 2
...
转自wikipedia:http://en.wikipedia.org/wiki/Continuation
http://www.shido.info/lisp/scheme_cc_e.html
http://blog.csdn.net/cnnzp/article/details/7832106
http://www.cs.utah.edu/~mflatt/past-courses/cs6520/public_html/s02/cps.pdf
http://en.wikipedia.org/wiki/Continuation-passing_style
https://cgi.soic.indiana.edu/~c311/doku.php?id=assignment-6
http://www.ibm.com/developerworks/cn/linux/l-advflow.html
http://martinfowler.com/bliki/Lambda.html
http://www.zhihu.com/question/20259086
非常好的解释:
http://www.ibm.com/developerworks/cn/linux/l-schm/part3/
Scheme语言相对Lisp语言的重要特征是提出并实现了continuation,这是让初学者最难理解,也是最让程序员心动的特征。那么Continuation到底是什么呢?在运算Scheme表达式过程中,我们必须跟踪两个东西:1、运算什么?2、什么与值相关?看一看下面表达式的计算: (if (null? x) (quote ()) (car x)) 。其中首先要运算的是(null? x),然后在这个值的基础上再运算(quote ())或(cdr x),要运算的是(null? x)而与值相关的是(quote ())和(car x);这里把与值相关的称为运算的continuation。我们看(null? x)表达式中与值相关的东西就有三个,1、表达式本身,结果为#t或#f;2、过程null?,它是一个lambda表达式;3、变量x,它应用当有一个值。那么上面的表达式里面有几个continuation呢?答案是六个,上面的表达式本身,我们说过的三个,car也是一个lambda表达式,还有它后面的x,也和值相关;而(car x)没有计算在内,因为它与上面表达式结果之一是相同的。
在任何表达式中,Continuation可以用一个叫call-with-current-continuation的过程来得到,多数情况下它被简化为call/cc。在guile的1.6.4或以前版本中,简化代码如下所示:
(define call/cc call-with-current-continuation)
而在其它的Scheme语言的实现版本中完全可以不用这样定义,而直接使用call/cc。
call/cc过程的唯一个参数应该是一个过程(或lambda表达式),而且这个过程只能有一个参数,continuation就绑定在这个参数上。看下面的代码:
guile> (define call/cc call-with-current-continuation)
guile> call/cc
#<procedure call-with-current-continuation (proc)>
guile> (call/cc (lambda (k) 5))
5
;;;此时过程参数k未用到,所以取过程的返回值5做为结果
guile> (call/cc (lambda (k) (* 5 (k 8))))
8
;;;此时过程参数k绑定为8,所以其结果为8
guile> (+ 2 (call/cc (lambda (k) (* 5 (k 8)))))
10
;;;此时结果在我们意料之中了 (个人注:想看看call/cc 直接调用中的参数到底是什么?
(define global 1)
(call/cc (lambda (k) (set! global k)( * 5 (k 8))))
(procedure? global) #t
(global (lambda (x) x))
#<procedure>
> (global 5)
5
)
可以利用call/cc这一特点,让它从一个循环中跳出来,这有点像C语言中的break,看下面的代码:
guile> (call/cc (lambda (return)
(for-each (lambda (x) (if (< x 0) (return x)))
'(99 88 77 66 55))
#t))
#t
其结果为#t,因为for-each运算过程中未出现过(< x 0)的情况,所以(lambda (return ) ...)的结果为#t,call/cc取此值为最终结果。
guile> (call/cc (lambda (return)
(for-each (lambda (x) (if (< x 0) (return x)))
'(11 22 33 44 -55 66 77))
#t))
-55
其结果为-55,因为当遇到小于0的数时,return就绑定x,call/cc就返回此值,即从for-each处理过程中跳出来,这是call/cc的重要功能之一,也是最基本的功能。
call/cc 还可以这样操作:
guile> (define foo #f)
guile> (call/cc (lambda (bar) (set! foo bar) 123))
123
guile> (foo 456)
456
guile> (foo 'abc)
abc
上面的操作中,call/cc绑定了lambda表达式的参数bar,而后我们又设定foo的值为bar,此时foo即为一个灵活的continuation;最后我们又让lambda表达式的值为123,所以整个call/cc表达式的值为123,如果我们不加这个123而直接结束这个表达式的话,此表达式就没有结果,不返回任何值。
当foo成为一个continuation后,我们可以象调用过程那样来调用它(当然它并不是过程),其后面绑定的值即为此continuation的结果,如上面的代码运行所示。
这段代码的前提是有call/cc的定义,如果你直接运行guile后就输入上面的代码是肯定会出错的。
http://c2.com/cgi/wiki?CallWithCurrentContinuation
非常的文章:
http://article.yeeyan.org/view/213582/179432
http://matt.might.net/articles/by-example-continuation-passing-style/
http://daweibalong.iteye.com/blog/1882795
scheme Continuation的更多相关文章
- 延续(continuation)
首先看下延续的定义: 续延是在运行中被暂停了的程序:即含有计算状态的单个函数型对象.当这个对象被求值时,就会在它上次停下来的地方重新启动之前保存下来的计算 在计算机科学和程序设计领域,延续是计算机程序 ...
- 理解 Continuation
理解 Continuation (2012-08-26 10:39:34) 终于,我也不能免俗地要来谈谈这几个 Schemer 的必谈话题(顺便山寨了一个标题). Scheme 是一门神奇的编 ...
- [Scheme]Understanding the Yin-Yang Puzzle
这题目确实比较杀脑细胞... 原题: (let* ((yin ((lambda (cc) (display "@") cc) (call-with-current-continua ...
- 与Scheme共舞
发表在<程序猿>2007年7月刊上.不log上写帖子不用考虑版面限制,所以这里的帖子比发表的啰嗦点.赵健平编辑,Jacky,和刘未鹏都给了我非常多帮助,在这里一并谢了.免费的Scheme实 ...
- scheme 阴阳谜题
本篇分析continuation的一个著名例子"阴阳迷题",这是由David Madore先生提出的,原谜题如下: (let* ((yin ((lambda (foo) (disp ...
- Teach Yourself Scheme in Fixnum Days 13 Jump跳转
Jumps One of the signal features of Scheme is its support for jumps or nonlocal control. Specificall ...
- call/cc 总结 | Scheme
call/cc 总结 | Scheme 来源 https://www.sczyh30.com/posts/Functional-Programming/call-with-current-contin ...
- 开始学习Scheme
开始学习Scheme 函数式编程(Functional Programming)是在MIT研究人工智能(Artificial Intelligence)时发明的,其编程语言为Lisp.确切地说,L ...
- scheme 之门
scheme 之门 开始之前 这是一篇 Scheme 的介绍文章. Scheme 是一个 LISP 的方言, 相对于 Common LISP 或其他方言, 它更强调理论的完整和优美, 而不那么强调实用 ...
随机推荐
- 解决SQL server不支持utf8,php却用utf8的矛盾问题
function convert2utf8($string) { return iconv("gbk","utf-8",$string); } function ...
- Jquery局部打印插件
局部打印插件 jquery.PrintArea.js js代码 (function ($) { var printAreaCount = 0; $.fn.printArea = fun ...
- 利用智能手机(Android)追踪一块磁铁(二)
在上一篇博客中提到了利用磁场强度推算传感器位置坐标的公式,下面就介绍怎么利用智能手机完成磁铁的追踪(任何具有磁感应器的装置均可以),这里主要是利用Android手机. 1:程序步骤: 首先将磁铁放置在 ...
- LeetCode198 House Robber
You are a professional robber planning to rob houses along a street. Each house has a certain amount ...
- phpWeb
Ruby on Rails框架在REST走得很前,开发时默认都按照RESTful风格搭建. <RESTful Web Services>是本好书 SOAP
- 绩效等级系统与MBO
1. 目标管理(MBO)在一定时期内(一般为一年)组织活动的期望成果,是组织使命在一定时期内的具体化,是衡量组织活动有效性的标准.由于组织活动个体活动的有机叠加,因此只有各个员工.各个部门的工作对组织 ...
- python - 文件
''' 模式 描述 r 以读方式打开文件,可读取文件信息. w 以写方式打开文件,可向文件写入信息.如文件存在,则清空该文件,再写入新内容 a 以追加模式打开文件(即一打开文件,文件指针自动移到文件末 ...
- java实现点名,并记录被点次数
java实现点名,并记录被点次数 import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStrea ...
- C函数的实现(strcpy,atoi,atof,itoa,reverse)
在笔试面试中经常会遇到让你实现C语言中的一些函数比如strcpy,atoi等 1. atoi 把字符串s转换成数字 int Atoi( char *s ) { int num = 0, i = 0; ...
- 关于js中select的简单操作,以及js前台计算,span简单操作
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...