若对续延有所认识 [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)))))
  (阴 阳))

下面就进入了玄学部分。

还记得老子的那句话吗?道生一,一生二,二生三,三生万物。万物负阴而抱阳,冲气以为和。

现在,可以将 Scheme 解释器视为「道」。将 (lambda (c) c) 视为「一」。将

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

视为「二」。

没错,就是二,因为「阴」和「阳」是同一个续延的不同称谓:

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

这种同一个事物具有不同名字的现象,《道德经》的开篇就通过「有」与「无」的关系指了出来,即「两者同出,异名同谓」。

(阴 阳) 这个表达式,就是「三」,表示了阴与阳之间的关系。有了这个关系,就可以产生万物——0 以外的全部自然数,这就是 (阴 阳) 的求值结果。再看一下(阴 阳) ,是不是万物负阴而抱阳,冲气以为和?

所以我说,老子的这句话与阴阳谜题一样,皆可谓神来之笔,举世无双。


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

你可能感兴趣的文章

载入中...
garfileo garfileo

4.7k 声望

发布于专栏

while(1) { }

只关心 C 与 Lisp/Scheme 程序的基本形状,其他的事关心不过来。

136 人关注