タコさんブログ

プログラミングメモと小言

すごい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 -}  ブロックコメント
  • Haskellではアポストロフィは通常の文字として扱われる。例えば、以下のような変数名、関数は有効である。
a' = 1
doubleNumber' x = 2 * x

※ GHCiで let をつけずに関数宣言をすると parse error on input ‘=’ とエラーがでるので注意すること。ファイルを読み込むのであれば、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)] となる