人気ブログランキング | 話題のタグを見る

Haskell のリスト操作

Haskell の説明を読むと、型云々の話から始まるので敬遠していたが、リスト操作に限って言えば非常に直感的で分かりやすい。これを使わないという法はない。

リスト操作で遊びながら、Haskellの癖に慣れてから、プログラムの書き方に取り組めばいいのではないだろうか。そのプログラムも、1)関数をプログラムする、2)パターン認識でプログラムする、という Haskell の特徴をつかむと、そう難しく感じられなくなってくる。

とりあえず、まずはリストをいじって遊ぶことから。どれもキー入力しているうちにすぐに覚えられるくらい直観的に理解できる。下のリストは追々充実させていくつもり。Google で 「Haskell リスト、関数」 で調べるといろいろ出てくる。これだけの知識でも平均値くらいは計算できる。

Hugs> sum [1,2,3,4] / 4.0
2.5

Haskell のリスト操作関数

1. リストの連結や切断

リストを連結する。
Hugs> [1,2,3] ++ [4,5,6]
[1,2,3,4,5,6]

リストの先頭に要素を追加する。
Hugs> 0:1:[2,3,4]
[0,1,2,3,4]

リストの先頭の値を取り出す。scheme の car と同じ
Hugs> head [1,2,3,4,5]
1

リストの2番目以降のリストを取り出す。scheme の cdr
Hugs> tail [1,2,3,4,5]
[2,3,4,5]

リストの最後の要素を取り出す。
Hugs> last [1,2,3,4,5]
5

リストの先頭から最後から2番目までのリストを取り出す。
Hugs> init [1,2,3,4,5]
[1,2,3,4]

リストの先頭の n 個の要素からなるリストを作る。
Hugs> take 3 [1,2,3,4,5]
[1,2,3]

条件に合う要素をリストの先頭から取り出す。条件が合わなくなったところで処理は終了する。
Hugs> takeWhile (<4) [1,2,3,4,5]
[1,2,3]

リストの先頭の n 個の要素を削除したリストを作る。
Hugs> drop 3 [1,2,3,4,5]
[4,5]

条件に合う要素をリストの先頭から削除する。条件が合わなくなったところで処理は終了する。
Hugs> dropWhile (<4) [1,2,3,4,5]
[4,5]

リストを n 番目の要素の位置で分断する。
Hugs> splitAt 3 [2,4,6,8,10]
([2,4,6],[8,10])

リストの先頭から条件に合う要素を集めたリストと、残りのリストのペアをつくる。
Hugs> span (< 4) [1..10]
([1,2,3],[4,5,6,7,8,9,10])

リストの先頭から条件に合わない要素を集めたリストと、残りのリストのペアを作る。
Hugs> break (>4) [1..10]
([1,2,3,4],[5,6,7,8,9,10])

2.リストの情報の取得

リストの n 番目の値を取り出す。先頭は 0 番目。
Hugs> [1,2,3,4,5] !! 3
4

リストの長さを取得する。
Hugs> length [1,2,3,4,5]
5

3.リストの生成

指定した範囲の整数の数列を作る。
Hugs> [3..10]
[3,4,5,6,7,8,9,10]

一定の間隔の整数の数列を作る。
Hugs> [1,3..10]
[1,3,5,7,9]

引数の要素 n 個からなるリストをつくる。
Hugs> replicate 5 3
[3,3,3,3,3]
Hugs> replicate 5 "hello"
["hello","hello","hello","hello","hello"]

4.リストの要素の加工

リストの要素の合計を計算する。
Hugs> sum [1,2,3,4,5]
15

リストの要素をすべて掛け合わせる。
Hugs> product [1,2,3,4,5]
120

リストの要素に関数を適用する。
Hugs> map (*2) [1,2,3,4,5]
[2,4,6,8,10]

リストの特定の要件をみたす要素を取り出す。
Hugs> filter odd [1..10]
[1,3,5,7,9]
Hugs> filter even [1..10]
[2,4,6,8,10]
Hugs> filter (>3) [1,2,3,4,5]
[4,5]

二つのリストの要素のペアを作る。
Hugs> zip [1,2,3] [4,5,6,7]
[(1,4),(2,5),(3,6)]

二つの要素のペアを作ってそのペアに演算を加える。
Hugs> zipWith (+) [1,2,3] [4,5,6]
[5,7,9]

リストのリストの要素を展開して要素のリストにする。
Hugs> concat [[1,2],[3],[4,5,6]]
[1,2,3,4,5,6]

リストの要素に演算を繰り返し適用する。
Hugs> 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)"
by tnomura9 | 2009-08-06 07:24 | Haskell | Comments(0)
<< Haskell の高階関数 使い捨てプログラムの作り方 >>