本篇分析continuation的一个著名例子"阴阳迷题",这是由David Madore先生提出的,原谜题如下:
(let* ((yin ((lambda (foo) (display "@") foo) (call/cc (lambda (bar) bar)))) (yang ((lambda (foo) (display "*") foo) (call/cc (lambda (bar) bar))))) (yin yang))
这里引用了http://www.ibm.com/developerworks/cn/linux/l-schm/part3/中的一些简化手段将其中的lambda表达式定义为过程,使其看起来更清晰:
(define bar (lambda (bar) bar)) (define foox (lambda (foo) (display "@") foo)) (define fooy (lambda (foo) (display "*") foo))
则上面的繁琐的表达式可以变成为:
(let* ((yin (foox (call/cc bar)))
(yang (fooy (call/cc bar))))
(yin yang))
将let*改变成let,使其进一步简化为:
(let ((yin (foox (call/cc bar))))
(let ((yang (fooy (call/cc bar))))
(yin yang)))
这里要说明的一点是,链接处介绍的最后一个简化也就是下面这个:
最后将let去掉,继而成为:
((foox (call/cc bar)) (fooy (call/cc bar)))
其实是错误的.
Ok,我们开始人肉解释器的运行:
(let ((yin (foox (call/cc bar))))
(let ((yang (fooy (call/cc bar))))
(yin yang)))
首先注意到(call/cc bar)会返回一个continuation,这里将第一行和第二行的continuation分别标识为c1,c2. 所以上面代码相当于:
(let ((yin c1));输出@ (let ((yang c2));输出* (yin yang)))
而(yin yang)就变成了(c1 c2).(c1 c2)相当于下面的调用
(define (c1 c2) (let ((yin (foox c2)));输出@ (let ((yang (fooy (call/cc bar))));输出* (yin yang))) )
用同样的手段,将(let ((yang (fooy (call/cc bar))))简化成(let ((yang c3))
(define (c1 c2)
(let ((yin c2))
(let ((yang c3))
(yin yang)))
)
所以最后的(yin yang)变成了(c2 c3),而(c2 c3)又相当于下面的函数调用:
(define (c2 c3) (let ((yin c1)) (let ((yang (fooy c3)));输出* (yin yang))) )
继续简化:(yin yang)变成(c1 c3),(c1 c3)又等价于:
(define (c1 c3) (let ((yin (foox c3)));输出@ (let ((yang (fooy (call/cc bar))));输出* (yin yang))) )
将(let ((yang (fooy (call/cc bar))))用(let ((yang c4))替代(yin yang)变成了(c3 c4)
(define (c3 c4) (let ((yin c2)) (let ((yang (fooy c4)));输出* (yin yang))) )
简化后(yin yang)变成(c2 c4),我们又回到这一步
(define (c2 c4) (let ((yin c1)) (let ((yang (fooy c4)));输出* (yin yang))) )
(yin yang)变成(c1 c4)
现在看下至今为止的输出:@*@**@***
好了,人肉scheme解释器的过程到此结束,因为这个程序会无休止的运行下去,所以人肉分析也是无休止的.
经过上述分析之后,相信对阴阳迷题和continuation又了更深刻的理解.