読者です 読者をやめる 読者になる 読者になる

無限リスト入門

これはAizu Advent Calendar 2014 22日目の記事です。

@ishi_kuro

@youxkei

告知ではCoqの記事を書くとかのたまってましたが、僕には無理でした。ごめんなさい。だれか僕に依存型教えてください。

ということで(?)、代わりにHaskellの無限リストのことを書きます。

無限リストの基礎の基礎

Haskellでは、次のような、無限の長さのリストを簡単に定義できます!!

ones = 1:ones

とっても気持ち悪い定義ですね。onesはどのように評価されていくのでしょうか。

ones 
---ones=1:onesで書き換える。
~> 1:ones
---やはりones=1:onesで書き換える。
~> 1: (1: ones)
---同上
~> 1:(1:(1:ones))
---以下同様
~> 1:1:1:1:1:.....

ということで、onesは、1が無限に続くリストになることがわかりますね。

しかし、無限の長さのリストなんて定義できても、どのように使えばいいのでしょうか。 実際に無限リストの値を使う場合には、有限の部分だけ使うことになります。

ones = 1:ones

main = do
    print $ take 10 ones

実行結果

[1,1,1,1,1,1,1,1,1,1,]

あらかじめ無限の長さでとして定義しておけば、使いたいときに使いたい長さ分だけ使える、というところが、 無限リストのメリットですね。

無限リストで等差数列、等比数列、無限級数

もうちょっと役に立ちそうな例として、高校数学でもお馴染みの等差数列と等比数列Haskellで定義してみたいと思います。

便利なiterateという関数があるお陰で、簡単なんですけどね。

iterate f x ~> [x, f x, f (f x), f (f (f x)), .....,  ]

ということで、初項1, 公比2の数列

[1, 2, 4, 8, 16, 32, ....]

という数列であれば、以下のように定義できます

iterate (*2) 1

だから、一般的な等差数列、等比数列も簡単なのがわかりますね。

arith_seq a d = iterate (+d) a
geo_seq a r = iterate (*r) a

無限級数も、scanlという関数のお陰で簡単です。

series seq = scanl (+) 0 seq

では、数列

[1, 1/2, 1/4, 1/8, 1/16, ...]

の和が、2に収束するということを確かめてみましょう。

プログラム

p e = (\x -> (2-x) >= e)
l = series (geo_seq 1 0.5)

takeWhile (p 0.001) l 

実行結果

[0.0,
1.0,
1.5,
1.75,
1.875,
1.9375,
1.96875,
1.984375,
1.9921875,
1.99609375,
1.998046875]

おまけ

有理数 p / qの小数表示に対応する無限リストはこんな感じ。

rat p q = (p `div` q) : rat ((p `mod` q) * 10) q