2

若对续延有所认识 [1],Scheme 的阴阳谜题就很容易理解了。

下面我先逐步构造出来这个谜题,然后从宏观的角度略微解释一下。事实上,也只能作宏观解释,否则纵身跳入续延的洞里,就更不容易弄清楚了。

从最简单的函数开始:

(lambda (c) c)

这个函数什么也不干,只是将自己接受的参数返回。

倘若将这个函数的求值表达式所形成的续延作为参数传递给了它自己,求值结果是这个续延本身,即:

((lambda (c) c) (call/cc (lambda (c) c)))

(call/cc (lambda (c) c)) 的作用在它出现的位置挖出来一个洞,从而构成一个续延 ((lambda (c) c) [])call/cc 的意思是,将当前续延作为参数去对一个函数进行求值。在这里,这个函数就是 call/cc 的参数 (lambda (c) c),而这个匿名函数的参数 c 就是 ((lambda (c) c) [])

为了直观一些,不妨将上面的函数求值表达式写成

((lambda (c) c) ((lambda (c) c) []))

由于这个表达式里面有一个洞,因此它是一个续延。接下来,将这个续延作为参数传递给自身,即

(((lambda (c) c) ((lambda (c) c) []))
 ((lambda (c) c) ((lambda (c) c) [])))

结果是什么?一条见首不见尾的神龙——无限层深的续延。因为对这个表达式的求值结果是一个层次更深的续延。Scheme 解释器不得不对这个更深的续延再次进行求值,每次求值,续延就更深一层。从洞的角度来看,每层续延里的洞衔接起来,构成了一根管子,在反复的求值过程中,这根管子也在逐渐延长。

现在用 call/cc 的形式替换这条神龙里的洞,结果就是:

(((lambda (c) c) (call/cc (lambda (c) c)))
 ((lambda (c) c) (call/cc (lambda (c) c)))) 

作为凡人,想看看这条神龙的形状,需要在续延里做两个标记:

(((lambda (c) (display #\@) c) (call/cc (lambda (c) c)))
 ((lambda (c) (display #\*) c) (call/cc (lambda (c) c))))
注: (display #\@) (display #\*) 分别表示在屏幕上打印 @* 这两个字符。

结果就得到了阴阳谜题。当前续延链自增长的过程会打印出:

@*@**@***@****@*****@******@*******@********@*********@**********... ...

形式上很复杂,对吧?可以简化一下:

(let ((阴 ((lambda (c) (display #\@) c) (call/cc (lambda (c) c))))
      (阳 ((lambda (c) (display #\*) c) (call/cc (lambda (c) c)))))
  (阴 阳))

[1] 续延,有什么难的……


garfileo
6k 声望1.9k 粉丝

这里可能不会再更新了。


引用和评论

0 条评论