LoginSignup
13
11

More than 5 years have passed since last update.

関数合成を機械的に扱う試み

Last updated at Posted at 2015-02-09

引数が複数ある時の関数合成は直感的な理解が困難です。あまり理解は追及しないで、機械的な式変形や解釈を試みました。

※ ちょっとした思い付きのようなもののため、どのような場面で有用なのかは未検証です。

この記事はHaskell 超入門シリーズの番外編です。

【追記 2016.7.1】似たような記事を見付けました。相互に独立ですが、観察すれば似たようなことに気付くのだと思いました。

【追記 2016.7.1】同じテーマを扱った記事です。

関数合成を進める

1引数の関数合成は簡単ですが、多引数に拡張できる形での式変形を導入します。

f :: a -> a
g :: a -> a

$.の書き換えでは、引数を括弧の外に出します。括弧がなければ式を囲みます。

  • f $ g x(f $ g x)(f . g) x

$が足りない時は、中置演算子をセクションで分断して$を挟みます。

  • 1 + 1(1 +) $ 1
  • (f . g) x((f .) $ g) x

2引数

gの引数を増やします。

f :: a -> a
g :: a -> a -> a

$.の書き換えでは、最後の引数を括弧の外に出します。$が足りなければ水増しします。

  1. f $ g x y
  2. 式を括弧で囲む: (f $ g x y)
  3. $.: (f . g x) y
  4. $を水増し: ((f .) $ g x) y
  5. $.: ((f .) . g) x y

3引数

gの引数を増やします。

f :: a -> a
g :: a -> a -> a -> a
  1. f $ g x y z
  2. 式を括弧で囲む: (f $ g x y z)
  3. $.: (f . g x y) z
  4. $を水増し: ((f .) $ g x y) z
  5. $.: ((f .) . g x) y z
  6. $を水増し: (((f .) .) $ g x) y z
  7. $.: (((f .) .) . g) x y z

モナド則3の右辺

少し違うパターンです。ラムダ式の部分を関数合成で書き換えます。

  1. m >>= (\x -> f x >>= g)
  2. $を水増し: m >>= (\x -> (>>= g) $ f x)
  3. 式を括弧で囲む: m >>= (\x -> ((>>= g) $ f x))
  4. $.: m >>= (\x -> ((>>= g) . f) x)
  5. ポイントフリースタイル: m >>= ((>>= g) . f)

モナド則については、次の記事が参考になるかもしれません。

関数合成を外す

逆方向の式変形です。1引数の場合から見ます。

f :: a -> a
g :: a -> a

.$の書き換えでは、引数を括弧の中に入れます。引数を適用するたびに合成が解除されると解釈できます。

  • (f . g) x(f $ g x)

セクションの後の$は外して、引数をセクションに適用します。

  • (1 +) $ 1(1 + 1)
  • ((f .) $ g) x((f . g)) x

無駄な括弧があれば整理します。

  • (f $ g x)f $ g x
  • (1 + 1)1 + 1
  • ((f . g)) x(f . g) x

2引数

gの引数を増やします。

f :: a -> a
g :: a -> a -> a

.$の書き換えは右から行います。最初の引数を括弧の中に入れます。

  1. ((f .) . g) x y
  2. .$(右から): ((f .) $ g x) y
  3. セクションに適用: ((f . g x)) y
  4. .$: ((f $ g x y))
  5. 括弧を整理: f $ g x y

3引数

gの引数を増やします。

f :: a -> a
g :: a -> a -> a -> a
  1. (((f .) .) . g) x y z
  2. .$(右から): (((f .) .) $ g x) y z
  3. セクションに適用: (((f .) . g x)) y z
  4. .$(右から): (((f .) $ g x y)) z
  5. セクションに適用: (((f . g x y))) z
  6. .$: (((f $ g x y z)))
  7. 括弧を整理: f $ g x y z

引数不足

.$の書き換え時に引数が足りないときは、式をラムダ式で包んで引数を補います。既にラムダ式で包まれていれば、引数は後ろに追加します。

  1. ((f .) .) . g
  2. ラムダ式化して引数を補う: \x -> (((f .) .) . g) x
  3. .$(右から): \x -> (((f .) .) $ g x)
  4. セクションに適用: \x -> (((f .) . g x))
  5. 引数を追加: \x y -> (((f .) . g x)) y
  6. .$(右から): \x y -> (((f .) $ g x y))
  7. セクションに適用: \x y -> (((f . g x y)))
  8. 引数を追加: \x y z -> (((f . g x y))) z
  9. .$: \x y z -> (((f $ g x y z)))
  10. 括弧を整理: \x y z -> f $ g x y z

.の数だけ引数が出て来ました。

モナド則3の右辺

上で書き換えたものを元に戻します。

  1. m >>= ((>>= g) . f)
  2. ラムダ式化して引数を補う: m >>= (\x -> ((>>= g) . f) x)
  3. .$: m >>= (\x -> ((>>= g) $ f x))
  4. セクションに適用: m >>= (\x -> ((f x >>= g)))
  5. 括弧を整理: m >>= (\x -> f x >>= g)

関数合成の解釈

関数合成がネストしているとどう解釈して良いのか戸惑います。意味の把握は難しいですが、先ほど示した式変形に沿えば、計算の流れを追うことはできます。

引数を適用するたびに合成が解除されて、計算が内部に移動します。式変形をしないで、適用の流れだけを追うイメージです。

apply.png

  1. 引数を適用: (((f .) .) $ g x) y z
  2. セクションに適用: (((f .) . g x)) y z
  3. 引数を適用: (((f .) $ g x y)) z
  4. セクションに適用: (((f . g x y))) z
  5. 引数を適用: (((f $ g x y z)))

モナド則3の右辺(関数合成版)

式変形せずに計算の流れを追います。

monad3.png

  1. mから値を取り出してfに適用: ((>>= g) . (m >>= f))
  2. fの戻り値をセクションに適用: (((m >>= f) >>= g))
  3. gに適用: (m >>= f) >>= g

計算過程を式変形で表現した結果、モナド則3の左辺に一致しました。

13
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
11