すごいHaskellたのしく学んでみる-その5
今回のテーマは、、、
- カリー化
- 高階関数
RubyやScalaをやった事があるので、
カリー化、高階関数は、割りとすんなり入って来ました。
カリー化
Haskellでは、関数はデフォルトでカリー化されています。
Haskellの全ての関数は、公式には引数を一つ取ることになっているそうです。
どういうことなのか、やってみたいと思います。
ある数字をもらって、それに3を足す関数を考えます。
addThree :: Int -> Int addThree x = x + 3
自分が、いつものノリで書くとこうなります。
しかし、Haskellの関数はカリー化されている、ということを考えると、
xが不要な事に気づきます。
直してみます。
addThree :: Int -> Int addThree = (+3)
※+は中置関数なので、()が必要なようです。
(+3)はIntの引数をもらえばIntの結果を返してくれます。
なので、上の2つの例は同じになります。
高階関数
高階関数とは、
第一級関数をサポートしているプログラミング言語において、関数を引数にしたり、あるいは関数を戻り値とするような関数のことである。
高階関数 - Wikipedia
簡単にいうと、mapとかfilterとかの事です。
それでは、カリー化の知識をもとに高階関数を使ってみたいと思います。
Prelude> map (*2) [1..20] [2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40] Prelude> zipWith (+) [1..10] [11..20] [12,14,16,18,20,22,24,26,28,30]
すごく簡単な例として、mapとzipWithをやってみました。
畳込み
Rubyでいうinjectのような関数として、
foldlとfoldrがあります。
lとrの違いは、左の要素から走査するか、右の要素から走査するか。
これは、結構大きな違いですね。
試してみたいと思います。
簡単な例として、mapを自分で実装してみたいと思います。
まず、リストを頭から順番に走査していく方法で、やってみます。
foldlを使います。
map' :: (a -> b) -> [a] -> [b] map' f xs = foldl (\ret a -> ret ++ [f a]) [] xs
これは非常に効率が悪いはずです。
[1] は 1: の糖衣構文のはずなので、
[1,2] ++ [3]というのは、
1:2:3:という状態を作り上げるために
[1,2]を 1:[2] → 1:2:[] と分解して行く必要があるのではないかと思います。
(もしかしたら、もっと効率的によしなにやってくれてるのかも?)
だとすると、リストは、
右側の値から順番に走査するほうが効率的なはず!
というわけで、foldrバージョンを考えてみます。
map'' :: (a -> b) -> [a] -> [b] map'' f (x:xs) = foldr (\a ret -> f a : ret) [] xs
※foldrとfoldlは、渡す関数の引数が逆になる事に注意!
※ちなみに、この「ret」とか[]のことをアキュームレーターというらしい。
うん、こっちのほうが、良い感じですね。
次回は、$を使った関数適用と関数合成にチャレンジ。