合成演算子は . (ドット):
f . g = ¥x -> f (g x)
つまり、右辺に引数を渡した結果を左辺に渡します。 ここから、以下のルールが導き出されます。
- 右辺の戻り値の型が左辺の引数と同じであること。
式で表すと、
(.) :: (b -> c) -> (a -> b) -> (a -> c)
引数に渡した関数を2度実行するtwiceという関数を定義する。
関数を2回実行するtwice関数:
twice :: (a -> a) -> (a -> a) twice f = f . f
このようになります。
次に、twiceとは別に、引数に関数のリストを受けて関数を返すcomposeという関数を定義します。
関数のリストを受け取って一つの関数に集約する:
compose :: [([a] -> [a])] -> ([a] -> [a]) compose = foldr (.) id
ここで、foldrという関数は
- 第1引数に関数を取る。
- 第2引数に初期値を取る。
- 第3引数にリストを取る。
という高階関数です。 わかり易い例では、0を初期値に整数のリストの合計値を算出する関数が挙げられます。 仮にこの関数をsumxと名付けると、以下の定義になります。
- foldr の利用例::
- sumx = foldr (+) 0 print $ sumx [1..10] -- => 55
Haskellでは()で演算子を囲むと関数として使えます。
このfoldrを使って、「関数のリストをすべて合成した関数」を作ることも可能です。 それが上記composedになります。 foldrを使って関数のリストを合成して関数を作る為には、「初期値としての関数」が必要になります。 こういう場合に役に立つのが恒等関数idです。
恒等関数idは、引数をそのまま返します。
例えば、
>>> f = even . id
>>> f 1 -- => False
このように、idはいかなる関数と合成しても合成相手をまったく変更しません。 つまり、foldrから関数のリストを合成するときの初期値にうってつけの関数なのです。
更に、composeを使って
- 引数のリストの各要素を2倍したリストを返す関数
- 引数のリストから偶数だけを取り出したリストを返す関数
を合成したcomposedという関数を作ります。
>>> composed :: Integral a => [a] -> [a]
>>> composed = compose [map (*2), filter even]
最後にcomposedをtwiceに渡して2度実行させる関数を作り、[1..10]を引数に渡して呼んでみます。
>>> twice composed [1..10]
-- => [8,16,24,32,40]
生成した関数から実際に結果を得ることができました。