すごいHaskell メモ
すごいHaskell 第1章
Haskell の勉強会に参加予定(途中参加)なので、「すごいHaskell 楽しく学ぼう」 第1章を読みながらのメモです。
ghciの起動と終了
- ghciの起動方法
$ ghci
- ghciの終了方法
Prelude> :quit
- ghciのヘルプ表示
Prelude> :?
関数
関数が2引数の場合、バッククオート(`)で囲むことで中置関数(infix function)に変換できる。
- 通常
div 4 2
4 `div` 2
中置オペレータ(infix operator)はカッコで囲むことで前置関数に変換できる。
- 通常
4 * 2
- 前置記法
(*) 4 2
Prelude> :l path_to_haskell_file
Haskellの基本
基本的で明らかに正しい関数を組み合わせて、より大きな関数を組み立てる
コメントの書き方
-- 行コメント {- comment -} ブロックコメント
a' = 1 doubleNumber' x = 2 * x
※ GHCiで let をつけずに関数宣言をすると
リスト入門
- リストの宣言
[1,2 3,4] 1:2:3:4:[]
- 連結
[1] ++ [2] -- 出力:[1, 2]
- リストの先頭に追加
1 : [2] -- 出力:[1, 2]
- リストの要素にアクセス
[1, 2, 3, 4, 5] !! 1 -- 出力:2
- >, >=, <, <= 辞書式でリストを比較できる
[1, 2] < [3, 4] -- 出力:True
- その他、リスト操作の関数
関数 | 説明 | 例 | 出力 |
---|---|---|---|
head | リストの先頭の要素を取り出す | head [1, 2, 3] | 1 |
tail | リストの先頭を除いたリストを取り出す | tail [1, 2, 3] | [2, 3] |
last | リストの最後の要素を取り出す | last [1, 2, 3] | 3 |
init | リストの最後の要素を取り除いたリストを取り出す | init [1, 2, 3] | [1, 2] |
length | リストの長さを返す | length [1, 2, 3] | 3 |
null | 空リストか調べる | null [] | True |
reverse | リストを逆順にする | reverse [1, 2, 3] | [3, 2, 1] |
take | 先頭から指定された数の要素を取り出したリストを返す。 | take 2 [1, 2, 3] | [1, 2] |
maximum | リスト中の最大の要素をそれぞれ返す | maximum [1, 2, 3] | 3 |
minimum | リスト中の最小の要素をそれぞれ返す | minimum [1, 2, 3] | 1 |
sum | リストの要素の和を返す | sum [1, 2, 3] | 1 |
product | リストの要素の積を返す | product [1, 2, 3] | 6 |
elem | リスト中に要素が含まれるか判定する | elem 1 [1, 2, 3] | True |
※ take関数はリストの要素数より大きい値を指定した場合、全リストを返す。 また、0を指定した場合、空リストを返す。
レンジ
- .. で列挙列のリストを作る
[1..10] -- 出力:[1,2,3,4,5,6,7,8,9,10]
- ステップを指定して、列挙列のリストを作る
[2, 4 .. 20] -- 出力:[2,4,6,8,10,12,14,16,18,20]
- 減少リストも可能。この場合、ステップを指定する必要がある。
[10,9..1] -- 出力:[10,9,8,7,6,5,4,3,2,1]
- その他、レンジ操作の関数
関数 | 説明 | 例 | 出力 |
---|---|---|---|
cycle | リストを受け取り、その要素を無限に繰り返す | take 10 (cycle [1,2,3]) | [1,2,3,1,2,3,1,2,3,1] |
repeat | 要素を受け取り、その要素を無限に繰り返すリストを返す | take 10 (repeat 5) | [5,5,5,5,5,5,5,5,5,5] |
replicate | リストの長さと、複製する要素を受け取り、単一の値からなるリストを返す | replicate 3 10 | [10,10,10] |
※ レンジで浮動小数点を使う場合、精度に限りがあるため、おかしな振る舞いをすることがあるので注意!
リスト内包表記
リストのフィルタリング、変換、組み合わせを行う表記法。これを読んでもよく分からないので、 例を考える。例えば1から20の中で偶数の値を取り出すリスト内包表記は以下のようになる。
[ 2 * x | x <- [1..10] ]
x <- [1..10]の部分でxの取る範囲を決めている。|の左側の部分で取り出した値に2倍して、 1から20の間の偶数が出力される。 |の右の部分にさらに条件(述語という)50から100の間で7で割った余りが3のリストの 内包表記は以下のようになる。
[ x | x <- [50..100], x `mod` 7 == 3 ]
タプル
タプルは括弧で囲み、要素をカンマで区切る。タプルは複数の違う型の要素をもつことができる。 - 例
(1, 2) (1, 'a')
サイズが2のタプルをペアと言う。 - ペアを操作する関数
関数 | 説明 | 例 | 出 力 |
---|---|---|---|
fst | 1つ目の要素を返す | fst (1,2) | 1 |
snd | 2つ目の要素を返す | snd (1,2) | 2 |
zip | 2つのリストからペアを要素とするリストを返す | zip [1,2] ["one","two"] | [(1,"one"),(2,"two")] |
直角三角形を見つける
以下の3条件を満たす斜辺をc、他の2辺をa、bとする 直角三角形(i.e. c2 = a2 + b2)を見つけるプログラムを書く。 1. 3辺の長さはすべて整数 2. 各辺の長さは10以下 3. 周囲の長さは24に等しい
- まず、各要素が10以下であるようなトリプル(タプルのサイズ3)を生成する
let triples = [ (a, b, c) | a <- [1..10], b <- [1..10], c <- [1..10] ]
- c2 = a2 + b2 を満たす述語を加える
let rightTriangles = [ (a, b, c) | a <- [1..10], b <- [1..10], c <- [1..10] , a^2 + b^2 == c^2 ]
- このままだと重複した直角三角形があるので、辺aが斜辺cを超えない、辺bが辺aを超えないように述語を書き直す
let rightTriangles = [ (a, b, c) | c <- [1..10], a <- [1..c], b <- [1..a] , a^2 + b^2 == c^2 ]
- 最後に、述語「周囲の長さが24に等しい」を加える
let rightTriangles = [ (a, b, c) | c <- [1..10], a <- [1..c], b <- [1..a] , a^2 + b^2 == c^2, a + b + c == 24 ]
- 上記の3条件を満たす直角三角形は [(8,6,10)] となる