若對續延有所認識 [1],Scheme 的陰陽謎題就很容易理解了。segmentfault
下面我先逐步構造出來這個謎題,而後從宏觀的角度略微解釋一下。事實上,也只能做宏觀解釋,不然縱身跳入續延的洞裏,就更不容易弄清楚了。函數
從最簡單的函數開始:code
(lambda (c) c)
這個函數什麼也不幹,只是將本身接受的參數返回。get
假若將這個函數的求值表達式所造成的續延做爲參數傳遞給了它本身,求值結果是這個續延自己,即:匿名函數
((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
爲了直觀一些,不妨將上面的函數求值表達式寫成call
((lambda (c) c) ((lambda (c) c) []))
因爲這個表達式裏面有一個洞,所以它是一個續延。接下來,將這個續延做爲參數傳遞給自身,即di
(((lambda (c) c) ((lambda (c) c) [])) ((lambda (c) c) ((lambda (c) c) [])))
結果是什麼?一條見首不見尾的神龍——無限層深的續延。由於對這個表達式的求值結果是一個層次更深的續延。Scheme 解釋器不得不對這個更深的續延再次進行求值,每次求值,續延就更深一層。從洞的角度來看,每層續延裏的洞銜接起來,構成了一根管子,在反覆的求值過程當中,這根管子也在逐漸延長。co
如今用 call/cc
的形式替換這條神龍裏的洞,結果就是:display
(((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] 續延,有什麼難的……