成らぬは人の為さぬなりけり

エンジニアライフをエンジョイする為のブログ

すごいHaskellたのしく学んでみる-その5

今回のテーマは、、、

RubyScalaをやった事があるので、
カリー化、高階関数は、割りとすんなり入って来ました。

カリー化

Haskellでは、関数はデフォルトでカリー化されています。
Haskellの全ての関数は、公式には引数を一つ取ることになっているそうです。

どういうことなのか、やってみたいと思います。


ある数字をもらって、それに3を足す関数を考えます。

addThree :: Int -> Int
addThree x = x + 3

自分が、いつものノリで書くとこうなります。

しかし、Haskellの関数はカリー化されている、ということを考えると、
xが不要な事に気づきます。

直してみます。

addThree :: Int -> Int
addThree = (+3)

※+は中置関数なので、()が必要なようです。

(+3)はIntの引数をもらえばIntの結果を返してくれます。
なので、上の2つの例は同じになります。

高階関数

高階関数とは、

第一級関数をサポートしているプログラミング言語において、関数を引数にしたり、あるいは関数を戻り値とするような関数のことである。

高階関数 - Wikipedia

簡単にいうと、mapとかfilterとかの事です。

前回書いたfind関数も高階関数です。


それでは、カリー化の知識をもとに高階関数を使ってみたいと思います。

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」とか[]のことをアキュームレーターというらしい。
うん、こっちのほうが、良い感じですね。


次回は、$を使った関数適用と関数合成にチャレンジ。