Haskell 入門


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)"