Continuation in LISP

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/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值