用call/cc合成所有的控制流结构

前言

我们都知道call/cc是最强大的控制流语句,几乎所有控制流语句(极少特殊的不能)都能用call/cc合成。那么我就来进行一下总结,用call/cc合成所有的控制流结构。如果您觉得有实现不正确的,欢迎在文章底部进行评论,我将对这篇文章进行更新。
除此之外,你还将学习到一些关于scheme宏编写的知识。

除最后一段代码以外均在racket v6.6下测试通过。

while语句

包含while,continue和break。

(require racket/stxparam)
(define-syntax-parameter break (syntax-rules ()))
(define-syntax-parameter continue (syntax-rules ()))
(define-syntax while
  (syntax-rules ()
    [(_ test body ...)
        (call/cc (lambda (k1)
                   (let ([t (void)])
                    (begin (call/cc (lambda (k2) (set! t k2)))
                           (syntax-parameterize
                               ([break (syntax-rules ()
                                         [(_) (k1 (void))])]
                                [continue (syntax-rules ()
                                         [(_) (t (void))])])
                             (when (not test) (break))
                             body ... (continue))))))]))

(let ([a 1])
  (while (< a 10)
         (set! a (+ a 1))
         (display a)))

(let ([a 1])
  (while (< a 10)
         (set! a (+ a 1))
         (when (= a 5) (break))
         (display a)))

(let ([a 1])
  (while (< a 10)
         (set! a (+ a 1))
         (when (= a 5) (continue))
         (display a)))

(let ([a 1])
  (while (< a 10)
         (set! a (+ a 1))
         (let ([b 1])
           (while (< b a)
                (display b)
                (display " ")
                (set! b (+ b 1))
                (when (= b 5) (break))
                )
         (display a)
         (display " "))))

第一个测试输出:2345678910
第二个测试输出:234
第三个测试输出:234678910
第四个测试输出:1 2 1 2 3 1 2 3 4 1 2 3 4 5 1 2 3 4 6 1 2 3 4 7 1 2 3 4 8 1 2 3 4 9 1 2 3 4 10

goto语句

(require racket/stxparam)
(define-syntax-parameter goto (syntax-rules ()))
(define-syntax prog
  (syntax-rules (label)
    [(_ "expanding" ((l1 code1 ...)(l codes ...) ...))
        ((call/cc (lambda (k)
                    (syntax-parameterize ([goto (syntax-rules ()
                                                  [(_ w) (k w)])]
                                                  )
                    (letrec ([l1 (lambda () (let () code1 ...))]
                             [l (lambda () (let () (void) codes ...))] ...)
                      l1)))))]
    [(_ "expanding" (a ... (l codes ...)) (label lname) rest ...)
        (prog "expanding" (a ... (l codes ... (lname)) (lname)) rest ...)]
    [(_ "expanding" (i ... (l codes ...)) code1 rest ...)
        (prog "expanding" (i ... (l codes ... code1)) rest ...)]
    [(_ xxx ...)
        (prog "expanding" ((start-label)) xxx ...)]))

(prog
      (goto k)
      (display "1")
      (label k)
      (display 2)
      )

exception

已经在上一篇文章Dynamic Scoping in Scheme提过,不再赘述。

Generators

很久之前写的东西,代码风格有些不一样。

;;;implement generators in scheme
;;;bugs fixed : Reset the Continuations
(define *meta-cont* (lambda (v) (error "No Top Level generator")))
(define-syntax (generator stx)
  (syntax-case stx ()
    [(generator expr ...)  #`(letrec (
                     [#,(datum->syntax #'generator `*cont*)
                      (lambda (v)
                        (reset expr ...)
                        )])
                     (lambda ()
                        (#,(datum->syntax #'generator `*cont*) (void))
                     ))]))

(define-syntax yield
  (lambda (stx)
    (syntax-case stx ()
      [(yield  v) #`(call/cc (lambda (k)
                            (set! #,(datum->syntax #'yield `*cont*) (lambda (va) (reset (k va))))
                               (*meta-cont* v)
                               ))]
       )))


(define-syntax reset
  (syntax-rules ()
    [(_ expr ...) (let ([preserved *meta-cont*])
                    (call/cc (lambda (k)
                               (set! *meta-cont* (lambda (v) (set! *meta-cont* preserved) (k v)))
                               (let ([result (begin expr ...)])
                                 (*meta-cont* result)
                                     ))))]))

;;example : yielding values
(define y (generator (yield 1)
                     (yield 2)
                     (yield 3)))
(y)
(y)
(y)

;;example : producer and consumer
(define (looper thunk) (thunk) (looper thunk))
(define product #f)
(define p (generator (for-each (lambda (f)
                                 (set! product f)
                                 (display "I have put ")
                                 (display f)
                                 (newline)
                                 (yield (c))) `(apple pea grape banana))))

(define c (generator (looper (lambda ()
                               (display "I have eaten ")
                               (display product)
                               (newline)
                               (set! product #f)
                               (yield (p))))))

(p)

;;example : generator makes infinite stream

(define i (let ([v 0])
              (generator (looper (lambda ()
                            (set! v (+ v 1))
                            (yield (stream-cons v (i))))))))
(define s (i))

(stream-ref s 0)
(stream-ref s 1)
(stream-ref s 2)
(stream-ref s 0)
(stream-ref s 100)


;;example : map generators

(define map-generator
  (lambda (f g)
    (generator (looper (lambda ()
                         (yield (f (g))))))))

(define a (map-generator (lambda (x) (+ 2 x))
           (generator (yield 1)
                     (yield 2)
                     (yield 3))))

(a)
(a)
(a)

tips:这样实现的generator可能会导致memory leaking。

coroutines,fibers

与generator原理类似,但略有不同,基本上每一本scheme语言的教材都有相关的代码,可以看the scheme programming language,4th edititon,就不给代码了。

Partial Continuation

shift/reset

用callcc实现的shift/reset会有效率问题,和上面的generator一样,可能会导致内存泄漏,建议用racket自带的(racket/control)。

(define *meta-cont* (lambda (v) (error "No Top Level reset")))
(define-syntax reset
  (syntax-rules ()
    [(_ expr ...) (let ([preserved *meta-cont*])
                    (call/cc (lambda (k)
                               (set! *meta-cont* (lambda (v) (set! *meta-cont* preserved) (k v)))
                               (let ([result (begin expr ...)])
                                 (*meta-cont* result))
                                 )))]))

(define-syntax shift
  (syntax-rules ()
    [(_ k expr ...) (call/cc
                     (lambda (k1)
                       (let* ([k (lambda (v) (reset (k1 v)))]
                              [v (begin expr ...)]
                              )
                         (*meta-cont* v))))]))

(reset (+ 1 (shift k (k (k 1)))))
(((reset (+ (shift a a) (shift b b))) 1) 3)

shift0/reset0

类似于shift/reset,把meta-cont换成了一个表。

(define *meta-cont* (list (lambda (v) (error "No Top Level rest0"))))
(define-syntax reset0
  (syntax-rules ()
    [(_ expr ...) (call/cc (lambda (k)
                             (set! *meta-cont* (cons k
                                                *meta-cont*
                                                ))
                             (let ([result (begin expr ...)]
                                   [c (car *meta-cont*)]
                                   [e (set! *meta-cont* (cdr *meta-cont*))]
                                   )
                                 (c result))
                                 ))]))

(define-syntax shift0
  (syntax-rules ()
    [(_ k expr ...) (call/cc
                     (lambda (k1)
                       (let* ([k (lambda (v) (reset0 (k1 v)))]
                              [c (car *meta-cont*)]
                              [e (set! *meta-cont* (cdr *meta-cont*))]
                              [v (begin expr ...)]
                              )
                         (c v))))]))

(reset0 (cons 1 (reset0 (shift0 k 2))))
(reset0 (cons 1 (reset0 (shift0 k (shift0 t 2)))))
(reset0 (+ 1 (shift0 k (k (k 1)))))
(reset0 (cons 1 (reset0 (reset0 (shift0 k (shift0 t 1))))))
*meta-cont*

dynamic-wind,unwind-protect

因为tspl上有实现的代码,我把它贴出来一下:(以下代码来自the scheme programming language,4th edititon

(define dynamic-wind #f)
 (let ((winders '()))
   (define common-tail
     (lambda (x y)
       (let ((lx (length x)) (ly (length y)))
         (do ((x (if (> lx ly) (list-tail x (- lx ly)) x) (cdr x))
              (y (if (> ly lx) (list-tail y (- ly lx)) y) (cdr y)))
             ((eq? x y) x)))))
   (define do-wind
     (lambda (new)
       (let ((tail (common-tail new winders)))
         (let f ((l winders))
           (if (not (eq? l tail))
               (begin
                 (set! winders (cdr l))
                 ((cdar l))
                 (f (cdr l)))))
         (let f ((l new))
           (if (not (eq? l tail))
               (begin
                 (f (cdr l))
                 ((caar l))
                 (set! winders l)))))))
   (set! call/cc
     (let ((c call/cc))
       (lambda (f)
         (c (lambda (k)
              (f (let ((save winders))
                   (lambda (x)
                     (if (not (eq? save winders)) (do-wind save))
                     (k x)))))))))
   (set! call-with-current-continuation call/cc)
   (set! dynamic-wind
     (lambda (in body out)
       (in)
       (set! winders (cons (cons in out) winders))
       (let ((ans (body)))
         (set! winders (cdr winders))
         (out)
         ans)))) 

engines

很遗憾,这个结构无法用call/cc合成。

recommend readings
1.the scheme programming language,chapter 5
2.applications of continuations,Dan P Friedman
3.schemewiki call-with-current-continuation & composable-continuations-tutorial
4.
lisp in small pieces,chapter 3

5.wiki:delimited continuations
6.okmij.org :Continuations and delimited control
7.matt might :Continuations by example: Exceptions, time-traveling search, generators, threads, and coroutines

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,784评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,745评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,702评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,229评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,245评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,376评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,798评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,471评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,655评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,485评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,535评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,235评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,793评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,863评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,096评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,654评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,233评论 2 341

推荐阅读更多精彩内容