言語ゲーム

とあるエンジニアが嘘ばかり書く日記

Twitter: @propella

ふつうの言語で Lazy list について考える。

こないだ Prolog を実装する時には随分 Haskell の遅延評価にお世話になりました。そこで、この大変便利な機能を普通の言語で使うにはどうしたら良いだろうかと色々調べると、Schemehttp://srfi.schemers.org/srfi-41/srfi-41.html というのに詳しく書いてある事が分かりました。他の言語でも色々やってる人居るみたいだけど、標準ライブラリになってるのは Scheme しか見つかりませんでした。

早速手元に gauche があるので実験します。gauche のは SRFI-40 とあってちょっと違うみたいだけど気にしない方向で。

gosh> (use util.stream)
#<undef>
gosh> (stream 1 2 3)
#<promise(stream) 0x695b40>
gosh> (stream-car (stream 1 2 3))
1
gosh> (stream-cdr (stream 1 2 3))
#<promise(stream) 0x695560>

stream-car で先頭要素を取って来て stream-cdr で残りのストリームを受け取るというのが基本的な使い方みたいです。

この stream という関数は、引き数を元にストリームを作るもので、

(stream-cons 1
	     (stream-cons 2
			  (stream-cons 3 stream-null)))

のような事をやっているみたいです。でもこれでは決まった数の要素しか作れず全然意味が無いので、関数を使って無限のフィボナッチ数列を作ってみます。

自分で面白いストリームを作るには stream-delay を使います。例えば永遠に続くフィボナッチ数列はこんな感じで。

(define fib
  (let loop ((a 0) (b 1))
    (stream-delay
     (stream-cons b (loop b (+ a b))))))

stream-delay はこのように無限ループにならないように途中で評価を止めるのに使うみたいです。早速先頭から10個取り出してみます。

gosh> (stream->list (stream-take fib 10))
(1 1 2 3 5 8 13 21 34 55)

これは面白い!ちなみに Haskell で書くとこうなります。take の順番が違うのはなんでだろ?

fib = loop 0 1
    where loop a b = b : loop b (a + b)

Fib> take 10 fib
[1,1,2,3,5,8,13,21,34,55]