第5章は高階関数について。高階関数はwikipediaによると「関数(手続き)を引数にしたり、あるいは関数(手続き)を戻り値とするような関数のこと」らしい。なるほど分からない。
カリー化関数
func :: Int -> Int -> Int
をfunc :: Int -> (Int -> Int)
というようにカッコを適当に付けて考えてやると、func 1 :: Int -> Int
と捉えることができる。
組み込みの関数の場合はカッコを付けてやることでカリー化できる。
divideByTen :: (Floating a) => a -> a divideByTen = (/ 10) -- 2引数なら`を付けてやれば2つ目の引数を使ってカリー化できる。 amariByTwo :: Int -> Int amariByTwo = (`mod` 2)
- 便利な関数
:t zip -- zip :: [a] -> [b] -> [(a, b)] {- zipしてから関数適用 -} :t zipWith -- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] {- 引数の順番入れ替え -} :t flip -- flip :: (a -> b -> c) -> b -> a -> c flippedFunc = flip func
map
:t map -- map :: (a -> b) -> [a] -> [b] map (+ 3) [1, 2, 3] -- [4, 5, 6]
filter
:t filter -- filter :: (a -> Bool) -> [a] -> [a] filter (> 2) [1, 2, 3] -- [3]
takewhile
:t takeWhile -- takeWhile :: (a -> Bool) -> [a] -> [a] takeWhile (\= ' ') "this is a party" -- "this"
ラムダ式
addThree = \x -> x + 3 -- 以下2つは同じ addThree = \ x y z -> x + y + z addThree = \x -> \y -> \z -> x + y + z addThree = (\x -> (\y -> (\z -> x + y + z)))
fold
foldlとfoldrがある。foldlは左畳み込み、foldrは右畳み込み。引数はfoldx (fn) (初期値) リスト
。
:t foldl -- foldl :: (a -> b -> a) -> a -> [b] -> a :t foldr -- foldr :: (a -> b -> b) -> b -> [a] -> b
上の定義よりfoldlはaがアキュムレータ、foldrはbがアキュムレータになっているので、引数に取る関数はfoldlがf(acc, val)の形に、foldlがf(val, acc)の形になっている。 また、foldlは壊れているという話(foldlを直そう)があるので、foldlを使うときは当分単細胞にData.List.foldl'使っておけば良さそう。
foldl1
、foldr1
はfoldの初期値なしバージョン。空のリストを受け取った場合に無意味なときに利用することを考えるといい。
無限リストとfold
foldrは右から、foldlは左から計算すると考えると、無限リストに対してはfoldlが使えるように見える。実際はhaskellは遅延評価するのでfoldrでは無限リストを扱えるが、foldlは扱えない。(このあたりは理解が足りないのでhaskell seqとか正格化とかを勉強する必要がある)
$
スペースで式を分けた時は左結合になるが$を使うと右結合になる。
sum (filter (> 10) (map (* 2) [2..10])) sum $ filter (> 10) $ map (* 2) [2..10] -- 80 ($ 3) (4 +) -- 7
関数合成
f(g x) = f . g x
である。
:t (.) (.) :: (b -> c) -> (a -> b) -> a -> c
ポイントフリースタイル
ポイントフリースタイル入門を読むとちょっと分かる。ポイントは.
のことではなく引数のこと。