Haskell
関数型プログラミング

すごいH本読んだからまとめ 9 「関数適用($),関数合成(.)」

はじめに

関数適用($), 関数合成(.)

関数適用演算子($)

どいういうものか

  • 関数をそのまま実行する関数として定義されている
  • ただし、この関数を実行するときの優先順位は最も低い
*Main Lib> :t ($)
($) :: (a -> b) -> a -> b


*Main Lib> (*) 3 $ 2 + 5
21
-- $ 関数は手前の関数(*)を実行するだけなので
-- カリー化された((*) 3) 関数を実行する
-- 適用順は最遅なので先に2+5(=7)が計算される
-- ((*) 3) 7 を実行すると21になる


-- 上の関数はこれと一緒
*Main Lib> (*) 3 (2 + 5)
21
-- ()いらずに関数が書けるようになったと思えば良い模様

複数個の($)を定義したときの実行順序

  • ($)は右結合性を持つ
  • 右結合性とは複数の演算子が重なったときに右側の演算子から実行されるということ
  • ($)は優先順最低の演算子なので他の関数が全て実行されたあとに右側から実行されていくと考えるとよい

下の関数を優先順位順に並べてみる

sum $ filter (> 10) $ map (*2) [2..10]

($) に囲まれていない関数を先に実行して結果を取得する(わかりやすさのため)

over10 = filter (> 10) -- 結果はカリー化された関数なので値を保存

*Main Lib> map (*2) [2..10]
[4,6,8,10,12,14,16,18,20] -- mapした結果

最初の関数に代入する

sum $ over10 $ [4,6,8,10,12,14,16,18,20]

右結合なので右側から関数を実施

*Main Lib> over10 $ [4,6,8,10,12,14,16,18,20]
[12,14,16,18,20]

最後に総和を計算して計算終了

*Main Lib> sum $ [12,14,16,18,20]
80

関数合成のための関数(.)

どういうものか

  • 関数と関数を結合する関数
  • 2番目の引数に与えた関数の戻り値を1番目の関数の引数に与える
-- 最初に受け取る関数の引数は一つだけみたい
-- 
*Main Lib> :t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c

-- ラムダ式を使ってf.gを定義する。
f . g = \x = f (g x)

複数個の(.)を定義した場合

関数合成も右結合なので、複数個の関数を合成することが可能

*Main Lib> (+) 3 . (*) 5 . (+) 2 $ 3
28
-- 関数適用($)と同じく右側の(.)から関数の実行がされていく

複数の引数を受け付ける場合

  • (.)演算子の定義が1引数を受け取るものになっている
  • その定義に合わせて関数を適用されるように($)演算子で調整する
-- 4つの引数を受け取る関数を事前に定義
plus4 w x y z = w + x + y + z

-- こんな感じで実行してみる
*Main> (+) 3 . (*) 5 . plus4times 1 2 3 4
-- エラーでる
<interactive>:30:1: error:
    ? Non type-variable argument in the constraint: Num (a -> c)
      (Use FlexibleContexts to permit this)
    ? When checking the inferred type
        it :: forall c a. (Num c, Num (a -> c)) => a -> c

-- ($)演算子を使って関数が適用される順番をコントロールする。
*Main> (+) 3 . (*) 5 $ plus4times 1 2 3 4
53 -- 「(+) 3 . (*) 5」を結合したものと「plus4times 1 2 3 4」の結果を計算する