このブログ記事では、関数型プログラミング(Haskellを含む)において重要な概念である関数合成の概念を紹介しています。

関数合成
関数合成は、二つの関数を取り、それらを組み合わせて新しい関数を作る概念です。h(x)=f(g(x)) Haskellでは、関数合成は以下のように表現されます。
( . ) :: (b -> c) -> (a -> b) -> a -> c
(f . g) === (\x -> f(g x))
これがどのように役立つのでしょうか?実際の例を見てみましょう。
descSort = reverse . sort
上記のように、関数を合成することで降順ソートを適用する関数を簡単に定義できます。また、map
に対しても非常に便利です。
map2D = map . map --- 最初のmapは行を反復処理するため、2番目のmapは行内の要素を反復処理するため
map2D (\x -> 2*x) [[1,2,3],[2,3,4]] --- Output: [[2,4,6],[4,6,8]]
このようにして、2次元リストに対するマッピングが可能です。n次元配列に対するマッピング関数も、n回のmap . map
の使用で構築できます。
ドル記号 ($)
一行で多くの関数を使う場面に遭遇したとき、多くの括弧を書くことにストレスを感じることがあります。 幸いにも、Haskellにはこの構文を簡略化する方法があります。
($) :: (a -> b) -> a -> b
f xs = map (\x -> x + 1) (filter (\x -> x > 1) xs)
f xs = map (\x -> x + 1) $ filter (\x -> x > 1) xs
括弧を書かずに、$
を使って別の関数を実行することを示すことができます。
(.) vs ($)
どちらを使うかについて混乱することがあるかもしれませんが、多くの場合、それはあなた次第です。ただし、
.
は二つの関数を組み合わせて新しい関数を作り出し、$
は二つの関数を連続して適用するための構文であることを
覚えておく必要があります。次の例が混乱を解消するのに役立つかもしれません。
add1 x = x + 1
add2 x = x + 2
--- For .
add3 = add1 . add2 --- VALID (x+2)+1
add3 x = add1 . add2 x --- INVALID! 関数と値(add2 xの結果)を.で組み合わせることはできません!
--- For $
add3 = add1 $ add2 --- INVALID! add1はNumを入力として受け取るだけで、関数を入力として認められません。
add3 x = add1 $ add2 x --- VALID y=(x+2), y+1=結果
個人的には、再利用可能な関数を構築するときは.
を、その組み合わせを一度だけ適用するときは$
を使うことを好みます。
クイズ
この記事では、学習した内容を確認するためのクイズを設けます。記事のメイン部分を読んだ後に、ぜひ自分で問題を解いてみることを強くお勧めします。各問題をクリックすると答えが表示されます。
リソース
- Philipp, Hagenlocher. 2020. Haskell for Imperative Programmers #8 - Function Composition. YouTube.