この章では、ラムダ、高階関数、などを説明しています。
今までは関数の引数を左辺で定義していました。
plusOne x = x + 1
main = do
print $ plusOne 100
101
この引数を右辺で定義することもできます。
plusOne = \ x -> x + 1
main = do
print $ plusOne 200
201
この右辺の式のことをラムダ式と呼びます。\ (バックスラッシュ)がギリシャ文字のラムダλに似ているからです。
型注釈とラムダ式を並べると書式が同じであることがわかります。
plusOne :: Int -> Int
plusOne = \x -> x + 1
main = do
print $ plusOne 300
301
ラムダ式は名前のない関数(無名関数)で、それを変数に束縛していると考えることもできます。
a = \x -> x + 1
main = do
print $ a 400
401
ラムダ式を変数に束縛しないで使うこともできます。
main = do
print $ (\x -> x + 1) 500
501
一度しか使わない関数にわざわざ名前をつけるのが面倒な時、ラムダ式は便利です。
複数の引数を持つ関数は次のように定義できます。
add1 x y = x + y
add2 = \x y -> x + y
add3 = \x -> \y -> x + y
main = do
print $ add1 100 100
print $ add2 200 200
print $ add3 300 300
200
400
600
実際には、add3が正式な書き方で、add1もadd2も、add3のシンタックスシュガー(糖衣構文)です。糖衣構文とは、複雑な記述を、簡単な記述に置き換える方法です。内部的には複雑な構文が使われています。
引数として関数を受け取ったり、戻り値として関数を返す関数を高階関数と言います。
higher func = func 200 100
add x y = x + y
sub = \x y -> x - y
main = do
print $ higher add
print $ higher sub
300
100
上のsubでは変数にラムダ式が束縛され、その変数がhigher関数に渡されています。変数を経由せずに、関数にラムダ式を直接渡すこともできます。
higher func = func 200 100
main = do
print $ higher $ \x y -> x + y
print $ higher $ \x y -> x - y
300
100
division x = \y -> x / y
main = do
let division5 = division 5
print $ division5 2
print $ (division 5) 2
print $ division 5 2
2.5
2.5
2.5
add x y = x + y
add' x = \y -> x + y
add'' = \x -> \y -> x + y
main = do
print $ add 20 30
print $ add' 20 30
print $ add'' 30 30
50
50
50
上記のように引数を分割して、関数をネストさせることをカリー化と言います。実際には、Haskellの関数が受け取ることができる引数は一つだけです。複数の引数を受け取った場合は、関数内部で自動的にカリー化が行われています。
add x y = x + y
main = do
let add20 = add 20
print $ add20 30
print $ (add 20) 30
50
50
50
上記のようにすれば、関数の一部の引数に値を与えておくことができます。
標準ライブラリで定義されている高階関数を紹介します。
第1引数の処理を第2引数のリストに適応した、新しいリストを作ります。同じ処理をするリスト内包表記もあげておきます。
main = do
print $ map (*2) [1..5]
print [x*2 | x <- [1..5]]
[2,4,6,8,10]
[2,4,6,8,10]
第2引数のリストから、第1引数の条件を満たす要素だけの、新しいリストを作ります。同じ処理をするリスト内包表記もあげておきます。
main = do
print $ filter (<=5) [1..10]
print $ [x | x <- [1..10], x <= 5]
[1,2,3,4,5]
[1,2,3,4,5]
リストの要素を左から1つずつ処理しながら集計した、新しいリストを作ります。同じ処理をするsum関数もあげておきます。
foldl :: 集計関数 -> 初期値 -> リスト -> 集計結果
main = do
print $ foldl (+) 0 [1..100]
print $ sum [1..100]
5050
5050
リストの要素を右から1つずつ処理しながら集計した、新しいリストを作ります。foldlと比べてみました。
main = do
print $ foldr (-) 0 [1..5]
print $ foldl (-) 0 [1..5]
3
-15