Haskell の関数
関数と引数
Haskell の関数を使うには、関数 引数1 引数2... のように関数名の後にスペースで区切って引数を並べていくだけだ。カッコも、コンマもいらない。
Prelude> div 6 2
3
Prelude> sum [1,2,3,4,5]
15
Prelude> sin (pi * 0.5)
1.0
関数の定義は関数名前と仮引数名前の後に = 記号を置き、その後に関数の定義を記述する。function などの関数定義のための特別な予約語はないので最初は戸惑うが、慣れると簡潔で気持ちがいい。
Prelude> double x = x * 2
Prelude> double 5
10
関数を複数行にわたって定義するときは、エディターで foo.hs のように拡張子に .hs をつけたファイルを作成し、それを ghci の :load コマンドで読み込む。また、ghci のコマンドラインからも :{ コマンドと :} コマンドの間に記述することができる。
Prelude> :{
Prelude| fact n =
Prelude| if n == 0 then 1
Prelude| else n * fact (n-1)
Prelude| :}
Prelude> fact 5
120
あるいは、ghic のコマンドライン1行に ; で区切って並べてもいい。
Prelude> a = 1; b = 2; func = a + b
Prelude> func
3
Haskell の関数はパターンで定義できるので、同名の関数を複数並べてもいい。その場合上に置かれた定義のパターンから順にパターンマッチが行われる。例えば階乗の関数の定義は次のように書ける。
Prelude> :{
Prelude| fact 0 = 1
Prelude| fact n = n * fact (n-1)
Prelude| :}
Prelude> fact 5
120
上のプログラムでは fact の引数が 0 より大きいときは、n * fact (n-1) が返され、0 のときは 1 が返される。したがって、
fact 3 = 3 * fact 2 = 3 * 2 * fact 1 = 3 * 2 * 1 * fact 0 = 3 * 2 * 1 * 1 = 6
のように fact 3 が展開されていって最後に階乗が計算される。Haskell の関数の実行は概ね上のような式の展開で行われるとイメージするとその動作が理解しやすくなる。また、引数を括弧 () で囲むと引数がリストの場合のパターンマッチを使うことができる。
Prelude> mysum [] = 0; mysum (x:xs) = x + mysum xs
Prelude> mysum [1,2,3]
6
Haskell ではこのように関数の定義の仕方にいろいろな方法がある。また、パターンマッチで定義できるので if then else などの制御構造文が少なくコンパクトで読みやすい定義ができる。
関数と演算子
演算子は普通の計算のように、引数の間に置く、カッコも使える。
Prelude> (1 + 2) * 3
9
Prelude> "hello, " ++ "world"
"hello, world"
また、‘ (バッククォート)で囲むと演算子のように使える関数もある。この機能で関数名を関数として使ったり、演算子として使ったりすることができる。
Prelude> 6 `div` 2
3
逆に + のような演算子も()で囲むと関数名のように使える。演算子も関数のように扱うことができる。
Prelude> (+) 1 2
3
関数と演算子の優先順位について言うと、関数のほうが演算子より、優先順位が上だ。
Prelude> length [1,2] + length [3,4,5]
5
モジュールに定義されている関数の使い方
関数の中には、モジュールを読み込まないと使えないものがある。たとえば、sort は Prelude を立ち上げてすぐには使えないが、
Prelude> sort [2,1,3]
<interactive>:26:1: error:
• Variable not in scope: sort :: [Integer] -> t
&bnsp; • Perhaps you meant ‘sqrt’ (imported from Prelude)
:m Data.List モジュールを読み込むと使えるようになる。
Prelude> Prelude> :m Data.List
Prelude Data.List> sort [2,1,8,7]
[1,2,7,8]
関数を連結する
関数は $ 演算子でつないで使うことができる。
Prelude Data.List> reverse $ sort [2,1,8,7]
[8,7,2,1]
無名関数
ラムダ記法を使えば無名関数が書ける。ラムダ記法では、バックスラッシュの後ろに変数リストをおき、関数の本体を -> の後ろに記述する。
Prelude> (\x -> x*x) 5 25 Prelude> (\x y -> x + y) 1 2 3
無名関数は変数に束縛できる。
Prelude> square = \x -> x * x Prelude> square 4 16
無名関数は特に map 関数の引数に使うと便利だ。
Prelude> map (\x -> x * x) [1..5] [1,4,9,16,25]
関数の部分適用
関数の引数の数より少ない数の引数を与えると、関数を部分適用した新しい関数が返される。
Prelude> add x y = x + y Prelude> add1 = add 1 Prelude> add1 2 3
2項演算の部分適用はセクションと呼ばれるが map の引数によく使われる。
Prelude> map (*2) [1..5] [2,4,6,8,10]
Huskell は説明はやたらと難しいが、使ってみると考えたことがそのままプログラムにできる感じで使いやすい。しばらく使ってその感触に慣れてから本を改めて読むと分かりやすくなる。自分で理解できた部分だけでいいからしばらく使ってみると、Haskell の楽しさが分かる。