ようこそ。睡眠不足なプログラマのチラ裏です。

Haskellのお勉強 その10

値としての関数

関数型プログラミング言語における関数ってのは値です。
値は変数を束縛することができます。したがって、関数は変数を束縛することができます。
また、関数は値なので、他の関数に引数として渡すことができます。


というわけで、高階関数の復習です。

Prelude> let square n = n^2
Prelude> map square [1..5]
[1,4,9,16,25]

んでもって、square関数がここでしか使わないような関数の場合、
いちいちsquareって変数に束縛する必要はないですよね。
というわけで、Haskellでは無名関数という、名無しの関数を適宜作ることができる。

Prelude>map (\n -> n^2) [1..5]
[1,4,9,16,25]

無名関数は複数のパターンマッチを列挙することができない以外は
通常の関数束縛と同様のパターンマッチを適用することができる。

たとえばタプルパターン

Prelude>map (\(x,y) -> x * y) [(2,3),(3,5)]
[6,15]

そんでもってデータコンストラクタパターン

Prelude> Import Char
Prelude Char>map (\(x:xs) -> toUpper x : xs) ["hogehoge","puyopuyo","pikopiko","aiueo"]
["Hogehoge","Puyopuyo","Pikopiko","Aiueo"]

関数合成

Haskellでは関数を合成することができます。
なにやら難しそうな感じもするが、別段難しくはない。


(.)関数の定義は以下のようになっている。

(.) :: (b -> c) -> (a -> b) -> (a -> c)

つまりこの定義では、(.)関数の第一引数の型が「(b -> c)」、
第二引数の型が「(a -> b)」、戻り値の型が「(a -> c)」である。
つまり(.)関数を用いて関すを合成をするには「第二引数で与えられた関数の戻り値の型(ここではb型)が、
第一引数で与えられた関数の引数として有効な型(つまりb型ね)でなければならない」
ということを表現しているわけですな。


つまり、(.)関数を用いた場合、f . gでは、
gを適用した後にfが適用されることになる。やってみる。

Prelude> reverse . reverse $ "abc"
"abc"
Prelude> (\xs -> (length . lines) xs) "aaaa\nbbbb\ncc\ndd"
4

おお、ちゃんと関数が合成されとりますね。
無名関数の場合でもちゃんと合成できてます。はい。


合成関数(.)の左右のオペランドを逆にした逆合成関数(>.>)を作成してみる。

(>.>) :: (a - b) -> (b -> c) -> (a -> c)
(>.>) = flip (.)

こんな感じの定義になる。


んで、試してみる

Prelude> let f = (2+) --section
Prelude> let g = (3*) --section
Prelude> let (>.>) = flip (.)
Prelude> (f.g) 3
11
Prelude> (g.f) 3
15
Prelude> (g>.>f) 3
11

おぉ簡単だーね。ただ逆になるだけだけど、こいつは面白いw
ちゃんと、(f.g) 3も (g>.>f) 3も「3倍してから2を加える関数」になってますね。


ちなみに、flip関数は、引数を2つ適用する関数を与えると、
引数の順番をひっくり返した関数を返してくれるという関数。うほっ!こりゃ便利w

flip :: (a -> b -> c) -> b -> a -> c
flip f x y = f y x

お疲れなので、今日はここまで。