ポイントフリースタイル
>2つのリストが等しいかどうかを比較したい。ただし、一方が他方より短い場合、短い方に合わせるとする。 “abcdef” == “abc” が等しいようなイメージ。こういうケースは結構あるんじゃないかと思う。
>
>どうするかっていうといろいろやり方がある気がするが、単純には zipWith で作れる。なかなか面白い。
>
>listEquals l1 l2 = and $ zipWith (==) l1 l2
>
>さて、これをポイントフリースタイルで書きたい。つまり、引数の l1 とか l2 とかを消したい。
>
>これが引数ひとつの場合は簡単だ。関数合成演算子の (.) があって、
>
>f . g = \a -> f (g a)
>
>になっているからこれを使う。上のように二引数だとどうだろう。実際に考えてみよう。まず、
>
>c2 f g = \a1 a2 -> f $ g a1 a2
>
>と書くところから始めよう。
>
>この定義から、まず a2 を削れることがわかるだろう。
>
>c2 f g = \a1 -> f . g a1
>
>ここからがちょっとポイントだが、これは次のように書ける。
>
>c2 f g = \a1 -> (f.) $ g a1
>
>この (f.) はセクションである。とすると、 a1 も追い出せる。
>
>c2 f g = (f.) . g
>
>うむ。
>
>したがって元の話題に戻ると、
>
>listEquals = (and.) . zipWith (==)
>
>である。美しい。
>
>さらにさらに。 g が3引数関数だとどうなるか。
>
>c3 f g = \a1 a2 a3 -> f $ g a1 a2 a3
>
>これは、
>
>c3 f g = \a1 a2 -> f . g a1 a2
>
>ゆえに
>
>c3 f g = \a1 a2 -> (f.) $ g a1 a2
>
>であるから、 c2 を使って、
>
>c3 f g = c2 (f.) g = ((f.).) . g
>
>となる。
>
>これを繰り返せば、任意の引数の数に対して、 (.) を使ってポイントフリースタイルで書けそうだ。逆に言うと、この形式のものは、 g が複数引数であるような関数合成であるということか。
>
>ではつぎに、 f も引数を取るケース。
>
>cb f g = \a1 a2 -> f a1 (g a2)
= \a1 -> f a1 . g > >ここまでは簡単だが、これはちょっと面倒くさい。 a1 を右側に持ってきたい。 flip が使えそうだ。 > >= \a1 -> flip (.) g $ f a1
= flip (.) g . f > >では f が2引数だとどうなるか。 > >cb2 f g = \a1 a2 a3 -> f a1 a2 (g a3)
= \a1 a2 -> f a1 a2 . g
= \a1 a2 -> flip (.) g $ f a1 a2
= \a1 -> flip (.) g . f a1
= (flip (.) g .) . f > >んじゃさらに、 f の第一引数に関数 g が適用され、第二引数には関数 h が適用されるような場合。 > >branch f g h = \a1 a2 -> f (g a1) (h a2)
= \a1 -> f (g a1) . h
= \a1 -> flip (.) h $ f (g a1)
= \a1 -> flip (.) h $ (f . g) a1
= flip (.) h . (f . g) > >f も引数欲しい。 > >branch' f g h = \a1 a2 a3 -> f a1 (g a2) (h a3)
= \a1 a2 -> f a1 (g a2) . h
= \a1 a2 -> flip (.) h $ f a1 (g a2)
= \a1 a2 -> flip (.) h $ (f a1 . g) a2
= \a1 -> flip (.) h . (f a1 . g)
= \a1 -> flip (.) h . (flip (.) g (f a1))
= \a1 -> flip (.) h . (flip (.) g . f) a1
= (flip (.) h .) . (flip (.) g . f) > >また、 h が2引数関数の場合。 > >branch'' f g h = \a1 a2 a3 -> f (g a1) (h a2 a3)
= \a1 -> (f (g a1) .) . h
= \a1 -> ((f . g) a1 .) . h
= \a1 -> flip (.) h ((f . g) a1 .)
= \a1 -> flip (.) h ((.) ((f . g) a1))
= \a1 -> flip (.) h (((.) . (f . g)) a1)
= flip (.) h . ((.) . (f . g)) > > >まとめ >: セクションに (.) を利用すると簡単に難読化できます。ポイントフリースタイルは、用法、用量を守って使いましょう。 > >……ところでこれ、合ってるのかな……。 >
= \a1 -> f a1 . g > >ここまでは簡単だが、これはちょっと面倒くさい。 a1 を右側に持ってきたい。 flip が使えそうだ。 > >= \a1 -> flip (.) g $ f a1
= flip (.) g . f > >では f が2引数だとどうなるか。 > >cb2 f g = \a1 a2 a3 -> f a1 a2 (g a3)
= \a1 a2 -> f a1 a2 . g
= \a1 a2 -> flip (.) g $ f a1 a2
= \a1 -> flip (.) g . f a1
= (flip (.) g .) . f > >んじゃさらに、 f の第一引数に関数 g が適用され、第二引数には関数 h が適用されるような場合。 > >branch f g h = \a1 a2 -> f (g a1) (h a2)
= \a1 -> f (g a1) . h
= \a1 -> flip (.) h $ f (g a1)
= \a1 -> flip (.) h $ (f . g) a1
= flip (.) h . (f . g) > >f も引数欲しい。 > >branch' f g h = \a1 a2 a3 -> f a1 (g a2) (h a3)
= \a1 a2 -> f a1 (g a2) . h
= \a1 a2 -> flip (.) h $ f a1 (g a2)
= \a1 a2 -> flip (.) h $ (f a1 . g) a2
= \a1 -> flip (.) h . (f a1 . g)
= \a1 -> flip (.) h . (flip (.) g (f a1))
= \a1 -> flip (.) h . (flip (.) g . f) a1
= (flip (.) h .) . (flip (.) g . f) > >また、 h が2引数関数の場合。 > >branch'' f g h = \a1 a2 a3 -> f (g a1) (h a2 a3)
= \a1 -> (f (g a1) .) . h
= \a1 -> ((f . g) a1 .) . h
= \a1 -> flip (.) h ((f . g) a1 .)
= \a1 -> flip (.) h ((.) ((f . g) a1))
= \a1 -> flip (.) h (((.) . (f . g)) a1)
= flip (.) h . ((.) . (f . g)) > > >まとめ >: セクションに (.) を利用すると簡単に難読化できます。ポイントフリースタイルは、用法、用量を守って使いましょう。 > >……ところでこれ、合ってるのかな……。 >