最新

val it : α → α = fun

<<  2006/06  >>

2006/06/13 ポイントフリースタイル

2つのリストが等しいかどうかを比較したい。ただし、一方が他方より短い場合、短い方に合わせるとする。 "abcdef" == "abc" が等しいようなイメージ。こういうケースは結構あるんじゃないかと思う。

どうするかっていうといろいろやり方がある気がするが、単純には zipWith で作れる。なかなか面白い。

listEquals l1 l2 = and $ zipWith (==) l1 l2

さて、これをポイントフリースタイルで書きたい。つまり、引数の l1 とか l2 とかを消したい。

これが引数ひとつの場合は簡単だ。関数合成演算子の (.) があって、

f . g = \a -&gt; f (g a)

になっているからこれを使う。上のように二引数だとどうだろう。実際に考えてみよう。まず、

c2 f g = \a1 a2 -&gt; f $ g a1 a2

と書くところから始めよう。

この定義から、まず a2 を削れることがわかるだろう。

c2 f g = \a1 -&gt; f . g a1

ここからがちょっとポイントだが、これは次のように書ける。

c2 f g = \a1 -&gt; (f.) $ g a1

この (f.) はセクションである。とすると、 a1 も追い出せる。

c2 f g = (f.) . g

うむ。

したがって元の話題に戻ると、

listEquals = (and.) . zipWith (==)

である。美しい。

さらにさらに。 g が3引数関数だとどうなるか。

c3 f g = \a1 a2 a3 -&gt; f $ g a1 a2 a3

これは、

c3 f g = \a1 a2 -&gt; f . g a1 a2

ゆえに

c3 f g = \a1 a2 -&gt; (f.) $ g a1 a2

であるから、 c2 を使って、

c3 f g = c2 (f.) g = ((f.).) . g

となる。

これを繰り返せば、任意の引数の数に対して、 (.) を使ってポイントフリースタイルで書けそうだ。逆に言うと、この形式のものは、 g が複数引数であるような関数合成であるということか。

ではつぎに、 f も引数を取るケース。

cb f g = \a1 a2 -&gt; f a1 (g a2)
       = \a1 -&gt; f a1 . g

ここまでは簡単だが、これはちょっと面倒くさい。 a1 を右側に持ってきたい。 flip が使えそうだ。

= \a1 -&gt; flip (.) g $ f a1
= flip (.) g . f

では f が2引数だとどうなるか。

cb2 f g = \a1 a2 a3 -&gt; f a1 a2 (g a3)
        = \a1 a2 -&gt; f a1 a2 . g
        = \a1 a2 -&gt; flip (.) g $ f a1 a2
        = \a1 -&gt; flip (.) g . f a1
        = (flip (.) g .) . f

んじゃさらに、 f の第一引数に関数 g が適用され、第二引数には関数 h が適用されるような場合。

branch f g h = \a1 a2 -&gt; f (g a1) (h a2)
             = \a1 -&gt; f (g a1) . h
             = \a1 -&gt; flip (.) h $ f (g a1)
             = \a1 -&gt; flip (.) h $ (f . g) a1
             = flip (.) h . (f . g)

f も引数欲しい。

branch' f g h = \a1 a2 a3 -&gt; f a1 (g a2) (h a3)
              = \a1 a2 -&gt; f a1 (g a2) . h
              = \a1 a2 -&gt; flip (.) h $ f a1 (g a2)
              = \a1 a2 -&gt; flip (.) h $ (f a1 . g) a2
              = \a1 -&gt; flip (.) h . (f a1 . g)
              = \a1 -&gt; flip (.) h . (flip (.) g (f a1))
              = \a1 -&gt; flip (.) h . (flip (.) g . f) a1
              = (flip (.) h .) . (flip (.) g . f)

また、 h が2引数関数の場合。

branch'' f g h = \a1 a2 a3 -&gt; f (g a1) (h a2 a3)
               = \a1 -&gt; (f (g a1) .) . h
               = \a1 -&gt; ((f . g) a1 .) . h
               = \a1 -&gt; flip (.) h ((f . g) a1 .)
               = \a1 -&gt; flip (.) h ((.) ((f . g) a1))
               = \a1 -&gt; flip (.) h (((.) . (f . g)) a1)
               = flip (.) h . ((.) . (f . g))

まとめ: セクションに (.) を利用すると簡単に難読化できます。ポイントフリースタイルは、用法、用量を守って使いましょう。

……ところでこれ、合ってるのかな……。