Continuation in LISP

来源:互联网 发布:js重定向url带参数 编辑:程序博客网 时间:2024/05/20 08:43

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编写的演示代码:

 

1. 如何使用Continuation:

 =========================================

(define the-continuation #f)

(define (test)
   (let ((i 0))
     ; call/cc 调用它的第一个函数参数, 将一个代表当前程序

     ; 执行点的“continuation变量”传递给函数参数。
     ;
     ; 在这个例子中,lambda函数将continuation变量k

     ; 赋值给变量 the-continuation.
     ;
     (call/cc (lambda (k) (set! the-continuation k)))
     ;
     ; 每当continuation执行的时候,我们都会回到此处执行下面的代码.
     (set! i (+ i 1))
     i))

 =========================================

上面的代码定义了一个函数test,以及设置了the-continuation作为程序执行点的保存。

下面看一下Continuation是如何工作的:

 

> (test)              ;调用函数test

1

> (the-continuation)  ;恢复到保存点继续执行
2

> (the-continuation)  ;再一次恢复到保存点继续执行
3
 

> (define another-continuation the-continuation) ;将continuation保存到另一个变量中


> (test)                   ;调用函数test, the-continuation中将保存新的程序恢复点。
1

> (the-continuation)  ;调用恢复到新的保存点继续执行
2

> (another-continuation)  ;恢复到旧的保存点继续执行

4


2. 通过Continuation实现Coroutines

========================================

   ;;; 线程调度队列.   ;;; 保存一个等待执行的线程的程序执行点(continuation)列表.   (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))    ;;; 运行一个新的线程 (proc).   (define (fork proc)     (call/cc      (lambda (k)        (enqueue k)  ;将(proc)执行前的点作为continuation保存到队列*queue*中        (proc))))        ;然后开始执行函数(proc)    ;;; 将运行权交给队列中的其他线程,自己进入等待队列    (define (yield)     (call/cc      (lambda (k)         ;保存放弃运行权时本线程所在的执行点(continuation)到*queue*中。        (enqueue k)        ((dequeue)))))  ;调用队列中第一个continuation,将执行跳转到那个continuation所保存的执行点,运行权交出。    ;;; 退出当前线程,如果所有线程以退出,则退出进程   (define (thread-exit)     (if (empty-queue?)         (exit)         ((dequeue))))========================================

以上代码定义了一个通过Continuation实现的Coroutine的线程调度

下面看看它是如何工作的:

   ;;; scheme中典型的thread函数的定义:    (define (do-stuff-n-print str)     (lambda ()       (let loop ((n 0))         (format #t "~A ~A/n" str n)         (yield)  ;每输出一次str,就将运行权交给其他线程         (loop (1+ 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 ...
参考资料:
http://www.scheme.com/tspl3/intro.html
http://en.wikipedia.org/wiki/Continuation
http://common-lisp.net/project/cl-cont/

原创粉丝点击