首页 > 代码库 > [Scheme]Understanding the Yin-Yang Puzzle

[Scheme]Understanding the Yin-Yang Puzzle

这题目确实比较杀脑细胞...

 

原题:

1 (let* ((yin
2          ((lambda (cc) (display "@") cc) (call-with-current-continuation (lambda (c) c))))
3        (yang
4          ((lambda (cc) (display "*") cc) (call-with-current-continuation (lambda (c) c)))))
5     (yin yang))

 

它会输出“@*@**@***@****@*****@******@*******@********...

 

怎么理解呢?

我的方案是移除call/cc,以纯函数的方案重新表示该问题,然后应用代换模型

 

首先,continuation是什么?

它是一个过程,以一个值为参数,这个值对应call/cc的返回;将实参应用在continuation这个过程上,应该相当于运行程序本身

 

原则上,continuation中应该包含整个调用栈上各帧及运算的一系列closure,以free variable的方式链接起来,不过由于本题及其简单,大大简化了continuation的近似。

 

首先,yang-continuation怎么表达?

1 (define (make-yang-continuation yin)
2   (lambda (yang) 
3     (putc #\*) 
4     (yin yang))
5   )

意思是,提供yin这个free variable,就返回一个continuation;如果你以参数yang(相当于call/cc的返回)调用continuation,就相当于运行程序(即原题中let*的第二部分)

 

再来看yin-continuation的表示:

1 (define yin-continuation
2   (lambda (yin)
3     (putc #\@)
4     (let ((yang-continuation (make-yang-continuation yin)))
5       (yang-continuation yang-continuation)))
6   )

这个continuation不需要free variable,因此直接define;它接收一个值yin(即call/cc的返回),再以它为free variable定义yang-continuation,最后,以yang-continuation自身作为参数来继续执行(对应原题中call/cc的参数(lambda (c) c))。

 

最后,运行整个程序:

1 (yin-continuation yin-continuation)

即,yin-continuation以continuation自身作为yin的初始值,运行整个程序。

 

完整代码:

 1 #lang racket
 2 
 3 (define n 0)
 4 (define (putc c)
 5   (cond
 6     ((< n 100) (set! n (+ n 1)) (display c))
 7     ((= n 100) (set! n (+ n 1)) (newline))
 8     (else ok))
 9   )
10 
11 (define (make-yang-continuation yin)
12   (lambda (yang) 
13     (putc #\*) 
14     (yin yang))
15   )
16 
17 (define yin-continuation
18   (lambda (yin)
19     (putc #\@)
20     (let ((yang-continuation (make-yang-continuation yin)))
21       (yang-continuation yang-continuation)))
22   )
23 
24 (yin-continuation yin-continuation)

这里putc打印无限输出串的前100个字符

 

因为只涉及纯函数,可以开始代换模型了。

下面我将yin-continuation记为y,make-yang-continuation记为m,(y x)可以代换,((m x1) x2)可以代换,前者代换的时候输出@,后者输出*:

(y y) =>

@ ((m y) (m y)) =>
@* (y (m y)) =>

@* @ ((m (m y)) (m (m y))) =>
@* @* ((m y) (m (m y))) =>
@* @** (y (m (m y))) =>

@* @** @ ((m (m (m y))) (m (m (m y)))) =>
@* @** @* ((m (m y)) (m (m (m y)))) =>
@* @** @** ((m y) (m (m (m y)))) =>
@* @** @*** (y (m (m (m y)))) =>

...

 

总算找到一个能把自己糊弄过去的说法...

 

ps. 有人看不懂scheme,给个lua的翻译版,应该没有理解障碍了(是移除call/cc后的版本,包含了我部分解题思路)。你觉得这谜题容易懂吗:

 1 local n = 0
 2 function putc(c)
 3     if n < 100 then 
 4         n = n + 1
 5         io.write(c)
 6     elseif n == 100 then 
 7         n = n + 1
 8         os.exit()
 9     end
10 end
11 
12 function make_yang_continuation(yin)
13     return function(yang)
14         putc(*)
15         yin(yang)
16     end
17 end
18 function yin_continuation(yin)
19     putc(@)
20     local yang_continuation = make_yang_continuation(yin)
21     yang_continuation(yang_continuation)
22 end
23 
24 yin_continuation(yin_continuation)