はじめに
-
すごいHaskellたのしく学ぼうを読んだのでちょっとずつまとめていきたい。まとめていきたい。
-
シリーズ
高階関数
そもそも高階関数とは
-
引数
や戻り値
に関数を渡せる関数 -
高級関数
ではなく高階関数
-- (a -> a)の関数を2回実行する関数
-- 1つめの引数に関数を与えたいので型の宣言を()でくくって優先順位をコントロール
applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)
-- 'a': ['b'] は "ab"
*Main> applyTwice ('a':) ['b']
"aab"
-- 関数の適用順を変更するflip関数を定義してみる。
flip' :: (a -> b -> c) -> b -> a -> c
flip' f y x = f x y
-- take関数を逆転してみる
-- take n [a] で「前からn個だけリストからアイテムを取得する」関数
flippedTake = flip' take
-- 先にリストを部分適用できる。
*Main> taken = flippedTake [1,2,3,4,5]
-- ほしい数をあとから取得する。
*Main> taken 3
[1,2,3]
- 関数を戻り値として扱う方法はカリー化を勉強したときに確認済み
Haskellに定義されている便利な高階関数たち
map
関数とリストを受け取って、リストのアイテムそれぞれに関数が適用されたリストを返す
*Main> :t map
map :: (a -> b) -> [a] -> [b]
*Main> map (+3) [1,2,3]
[4,5,6]
*Main> map (+3) (map (+3) [1,2,3])
[7,8,9]
-- 関数のリストも返せる
*Main> pluslist123 = map (+) [1,2,3] -- [((+) 1), ((+) 2), ((+) 3)]のリスト
*Main> (head pluslist123) 5 -- ((+) 1) 5
6
filter
関数とリストを受け取って、受け取った関数がTrueを返す値のみを取得したリストを返す。
*Main> :t filter
filter :: (a -> Bool) -> [a] -> [a]
*Main> filter ((==) 3) [1,2,3,1,2,3]
[3,3]
foldl(r)
- foldlをリストに適用する場合、下記の通り関数が実行される
- リストの先頭から値を1つ取ってきて関数を適用する
- 適用した結果は次の計算に使われる
- 最後にすべてのリストを走査した結果を返す
- 上記のような走査を
畳み込み
という。 - 畳み込みの初期値をアキュミュレータという
- foldlは左から畳み込む、foldrは右から畳み込む
- 他のプログラミングでreduceって名前で定義されてることもある
foldl
*Main> :t foldl
foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b
-- (Foldable t)は型クラス 直訳すると「畳み込める」
-- []で定義されるリスト以外にも畳み込める「型t」ならfoldlを適用して良いということ
-- この場合0がアキュミュレータ
*Main> foldl (+) 0 [1,2,3]
6
-- 真偽値と数を受け取って受け取った真偽値と奇数かどうかで演算を行う関数
isOddAnd :: (Integral a) => Bool -> a -> Bool
isOddAnd bool x = (x `mod` 2) /= 0 && bool
-- foldl関数を適用してすべての値が奇数かチェックできる
*Main> foldl isOddAnd True [1,3,5,7,9]
True
*Main> foldl isOddAnd True [1,3,5,7,10]
False
-- 部分適用して新しい関数を作る
*Main> isAnyOdd = foldl isOddAnd True
*Main> isAnyOdd [1,3,5,7,9]
True
*Main> isAnyOdd [1,3,5,7,10]
False
foldr
*Main> :t foldr -- foldrは引数の順番が変わる
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
-- (:) を使って空のリストにリストの値を代入できる。
*Main> foldr (:) [] [1,2,3,4,5]
[1,2,3,4,5]
-- 偶数なら2倍したリストを返す関数
evenDoubleAppend :: (Integral a) => a -> [a] -> [a]
evenDoubleAppend x
| x `mod` 2 == 0 = (2 * x :)
| otherwise = (x :)
*Main> foldr evenDoubleAppend [] [1,2,3,4,5]
[1,4,3,8,5]
foldl(r)の派生
- 畳み込みの途中経過をすべて返す
scanl(r)
関数 - 畳込みの初期値(アキュミュレータ)を操作するものの先頭にする
foldl1
,foldr1
関数
*Main> :t scanl
scanl :: (b -> a -> b) -> b -> [a] -> [b]
*Main> scanl (+) 0 [1,2,3]
[0,1,3,6]
*Main> :t scanr
scanr :: (a -> b -> b) -> b -> [a] -> [b]
*Main> scanr (+) 0 [1,2,3]
[6,5,3,0]
*Main> :t foldl1
foldl1 :: Foldable t => (a -> a -> a) -> t a -> a
*Main> foldl1 (+) [1,2,3]
6
*Main> :t foldr1
foldr1 :: Foldable t => (a -> a -> a) -> t a -> a
*Main> foldr1 (+) [1,2,3]
6