Haskell のリスト操作
Haskell の説明を読むと、型云々の話から始まるので敬遠していたが、リスト操作に限って言えば非常に直感的で分かりやすい。これを使わないという法はない。
リスト操作で遊びながら、Haskellの癖に慣れてから、プログラムの書き方に取り組めばいいのではないだろうか。そのプログラムも、1)関数をプログラムする、2)パターン認識でプログラムする、という Haskell の特徴をつかむと、そう難しく感じられなくなってくる。
とりあえず、まずはリストをいじって遊ぶことから。どれもキー入力しているうちにすぐに覚えられるくらい直観的に理解できる。たとえば、リストの平均値の計算は次のようにできるが、とくに説明もいらないだろう。
Prelude> sum [1,2,3,4] / 4.0
2.5
1. リストの連結や切断
リストを連結する。
Prelude> [1,2,3] ++ [4,5,6]
[1,2,3,4,5,6]
リストの先頭に要素を追加する。
Prelude> 0:1:[2,3,4]
[0,1,2,3,4]
リストの先頭の値を取り出す。scheme の car と同じ
Prelude> head [1,2,3,4,5]
1
リストの2番目以降のリストを取り出す。scheme の cdr
Prelude> tail [1,2,3,4,5]
[2,3,4,5]
リストの最後の要素を取り出す。
Prelude> last [1,2,3,4,5]
5
リストの先頭から最後から2番目までのリストを取り出す。
Prelude> init [1,2,3,4,5]
[1,2,3,4]
リストの先頭の n 個の要素からなるリストを作る。
Prelude> take 3 [1,2,3,4,5]
[1,2,3]
条件に合う要素をリストの先頭から取り出す。条件が合わなくなったところで処理は終了する。
Prelude> takeWhile (<4) [1,2,3,4,5]
[1,2,3]
リストの先頭の n 個の要素を削除したリストを作る。
Prelude> drop 3 [1,2,3,4,5]
[4,5]
条件に合う要素をリストの先頭から削除する。条件が合わなくなったところで処理は終了する。
Prelude> dropWhile (<4) [1,2,3,4,5]
[4,5]
リストを n 番目の要素の位置で分断する。
Prelude> splitAt 3 [2,4,6,8,10]
([2,4,6],[8,10])
リストの先頭から条件に合う要素を集めたリストと、残りのリストのペアをつくる。
Prelude> span (< 4) [1..10]
([1,2,3],[4,5,6,7,8,9,10])
リストの先頭から条件に合わない要素を集めたリストと、残りのリストのペアを作る。
Prelude> break (>4) [1..10]
([1,2,3,4],[5,6,7,8,9,10])
2.リストの情報の取得
リストの n 番目の値を取り出す。先頭は 0 番目。
Prelude> [1,2,3,4,5] !! 3
4
リストの長さを取得する。
Prelude> length [1,2,3,4,5]
5
3.リストの生成
指定した範囲の整数の数列を作る。
Prelude> [3..10]
[3,4,5,6,7,8,9,10]
一定の間隔の整数の数列を作る。
Prelude> [1,3..10]
[1,3,5,7,9]
引数の要素 n 個からなるリストをつくる。
Prelude> replicate 5 3
[3,3,3,3,3]
Prelude> replicate 5 "hello"
["hello","hello","hello","hello","hello"]
4.リストの要素の加工
リストの要素の合計を計算する。
Prelude> sum [1,2,3,4,5]
15
リストの要素をすべて掛け合わせる。
Prelude> product [1,2,3,4,5]
120
リストの要素に関数を適用する。
Prelude> map (*2) [1,2,3,4,5]
[2,4,6,8,10]
リストの特定の要件をみたす要素を取り出す。
Prelude> filter odd [1..10]
[1,3,5,7,9]
Prelude> filter even [1..10]
[2,4,6,8,10]
Prelude> filter (>3) [1,2,3,4,5]
[4,5]
二つのリストの要素のペアを作る。
Prelude> zip [1,2,3] [4,5,6,7]
[(1,4),(2,5),(3,6)]
二つの要素のペアを作ってそのペアに演算を加える。
Prelude> zipWith (+) [1,2,3] [4,5,6]
[5,7,9]
リストのリストの要素を展開して要素のリストにする。
Prelude> concat [[1,2],[3],[4,5,6]]
[1,2,3,4,5,6]
リストの要素に演算を繰り返し適用する。
Prelude> foldr (+) 0 [1..10]
55
5.リストの再帰関数
右再帰型の再帰関数は foldr で記述できる。
mysum [] = 0
mysum (x:xs) = x + mysum xs
のような右再帰型の再帰関数は foldr を使って記述できる。
foldr (+) 0 [1,2,3,4,5]
= (1 + (2 + (3 + (4 + (5 + 0)))))
= 15
左再帰型の再帰関数は foldl で記述できる。
fact [] = 1
fact (xs:x) = (fact xs) * x
のような左再帰型の再帰関数は foldl で記述できる。注:(xs:x) というパターンは仮想のパターンで Haskell では使えない。
foldl (*) 1 [1,2,3,4,5]
= (((((1 * 1) * 2) * 3) * 4) * 5)
= 120
foldl' は foldl を正格で計算する(括弧の中を先に計算する)ので計算速度やメモリ消費の上で利点がある。
Prelude> :m +Data.List
Prelude Data.List> foldl' (+) 0 [1..2000000]
2000001000000
畳み込み演算の動作は分かりにくいが、どういう演算になるのかは機械に聞くことができる。
Prelude> foldr (\x y -> concat["(",x,"+",y,")"]) "0" (map show [1..10])
"(1+(2+(3+(4+(5+(6+(7+(8+(9+(10+0))))))))))"
Prelude> foldl (\x y -> concat["(",x,"+",y,")"]) "0" (map show [1..10])
"((((((((((0+1)+2)+3)+4)+5)+6)+7)+8)+9)+10)"