遅きに失した感もあるけれど、世間ではズンドコキヨシというのが流行っているらしいので Scheme で書いてみた。処理系は Gauche を仮定しているが、他の R7RS 処理系に移植するのはそれほど難しくないと思う。
コードを書く前に考えてみると、下のような状態機械が思い浮かぶ。
ズン ズン ズン ズン ドコ
--> 0 -+-----> 1 ----> 2 ----> 3 ----> 4 ----> ((キ・ヨ・シ!))
^ |* |* |* |* |*
| | | | | |
+--+-------+-------+-------+-------+
末尾呼び出し最適化のある言語の気持ちになると、状態は手続きであり、手続きの末尾呼び出しは状態遷移なので、こんな風に書いてみた。入力はストリームとして受け取るものとした。
(use srfi-27)
(use util.stream)
(define zun "ズン")
(define (zun? x)
(equal? x zun))
(define doko "ドコ")
(define (doko? x)
(equal? x doko))
(define (init strm)
(cond ((zun? (stream-car strm))
(zun1 (stream-cdr strm)))
(else
(init (stream-cdr strm)))))
(define (zun1 strm)
(cond ((zun? (stream-car strm))
(zun2 (stream-cdr strm)))
(else
(init (stream-cdr strm)))))
(define (zun2 strm)
(cond ((zun? (stream-car strm))
(zun3 (stream-cdr strm)))
(else
(init (stream-cdr strm)))))
(define (zun3 strm)
(cond ((zun? (stream-car strm))
(zun4 (stream-cdr strm)))
(else
(init (stream-cdr strm)))))
(define (zun4 strm)
(cond ((doko? (stream-car strm))
"キ・ヨ・シ!")
(else
(init (stream-cdr strm)))))
init
から始めて、受理状態になったら "キ・ヨ・シ!"
を返して停止することにする。
入力は、乱数生成器を使って、「ズン」と「ドコ」をランダムに並べたストリームを生成する。
(define (zundoko rng)
(let ((rint (random-source-make-integers rng)))
(stream-tabulate -1
(lambda (_)
(case (rint 2)
((0) zun)
(else doko))))))
途中経過も見たいので、入力ストリームに値を表示する手続きを stream-map
したものを渡しつつ、戻り値を print
する(Gauche のストリーム、すなわち SRFI-41 のストリームは even ストリームで、 stream-car
を取ったときに計算が実行されるのでこれでうまく行く)。
(define (d x)
(display x)
x)
(print (init (stream-map d (zundoko (make-random-source)))))
(exit 0)
こんな感じの結果が出力される。
ドコズンドコドコズンドコドコズンドコズンズンドコズンズンドコドコドコドコドコドコズンドコドコドコドコズンズンドコドコズンズンズンズンズンドコドコドコドコドコドコドコドコズンズンドコズンドコドコドコズンドコドコドコズンズンズンドコズンズンズンドコドコズンズンズンドコズンズンズンドコドコドコドコズンズンドコドコズンズンドコズンズンズンズンドコキ・ヨ・シ!