1. 関数合成の例
Haskell では (.) を用いると、2つの関数を合成できる。
例えば、与えられた数値に1加える関数 add1 と、与えられた数値を2倍する mul2 がある。
add1 x = x + 1 mul2 x = x * 2
100を2倍した後、1加えるには、
*Main> add1 $ mul2 100 201
100に1加えた後、2倍するには、
*Main> mul2 $ add1 100 202
上記の関数を(.) を使い、合成したい場合、
add1mul2 = add1 . mul2 mul2add1 = mul2. add1
これを使うには、
main = do print $ add1mul2 100 --- 201 print $ mul2add1 100 --- 202
(.) を使うと、シンプルな関数を組み合わせて、複雑な関数を定義することができる。
2. 関数合成を理解するには、部分適用について先に知るのが良い
「ふつうのHaskellプログラミング」で、はじめに戸惑ったのが、この
- 「8.2 関数合成」 (p198)
の説明。
- 「8.3 部分適用」 (p202)
の解説の前にあったので、すんなりと理解できなかった。
同書において、「関数合成」について、以下のように述べられている。
(.) 関数は、二つの関数を合成する関数です。「合成する」とは、「2つの関数を順番に適用する新しい関数を作る」ということです。(p198)
関数と関数を合成?
(.) を適用すると関数が返されるというのは、どういう意味だろう?(@_@;)
3. 関数合成 (.) も関数の一種
はじめて
.
を見たとき、この記号は、Haskell で「引数を省略して書くための、特別な表記」なのかと思った。
先ほどの例で言えば、(.) を使わずに add1mul2 関数を定義するなら、以下のように書ける。
add1mul2 x = add1 $ mul2 x
この定義と (.) を使った定義を比べると、
add1mul2 = add1 . mul2add1mul2 の引数が消されてしまったかのように見える。
同書(p199) の説明に、関数 (.) の型が書かれてる。
(.) :: (b –> c) –> (a –> b) –> (a –>c)
つまり (.) は、
「引数として 2 つの関数を与えると、関数が返される関数である」
というように、関数に過ぎない。
ただし、「部分適用」 に関する知識がない状態で、この説明を理解することができなかった。
合成する前にあった、関数の引数が消えてしまっている。これはどういうことだろうか?
という疑問を持った。
4. 関数を合成する関数に対して「部分適用」
上記を理解するには、「部分適用」について把握しておく必要がある。
(.) の実装を見てみる。Haskell Code by HsColour によると、
(.) :: (b -> c) -> (a -> b) -> a -> c (.) f g x = f (g x)
関数の型宣言における、最後の
a –> c
が括弧で囲まれていない。なぜなら、関数の宣言は右結合であるため、括弧が必要ないから。
4.1.2 型の構文 によると、
関数の型 は t1 -> t2 という形式で、これは、型 (->) t1 t2 と同等である。関数の矢印は右結合である。
意味は全く同じだけれど、最後の括弧がない方が、(.) 関数を
「3 つの引数を与えると、値が返ってくる関数」
という意識で見ることができる。
部分適用について理解があれば、
(.) に対して、二つの関数を与えると、あと一つ引数を与えれば値が返される関数
となり、部分適用した状態であるということが分かる。
5. (.) 関数を使い、型を確認する
例えば、
- 適当に関数 f, g を定義し、
- 2つの関数を (.) 関数に部分適用し、
- 型を確認してみる。
Prelude> let f x = x + 10 Prelude> let g x = x * 2 Prelude> :t (.) f g (.) f g :: (Num a) => a -> a
関数 f, g を (.) に部分適用した型は、
あと一つ引数として値を与えれば、値が返ってくる関数
であることが示される。つまり、
f . g
という式は、 (.) 関数に対して、引数を 2 つ与え、部分適用した状態である。
(.) 関数の定義に戻り、3 つ目の引数 x を見れば、
(.) :: (b -> c) -> (a -> b) -> a -> c (.) f g x = f (g x)
これが先ほど「消えてしまった」と感じた、関数の引数であることが分かる。
1コメント:
過去のエントリへのコメント失礼します。『ふつうのHaskellプログラミング』 P199の解説に納得いかなくて、これって厳密には部分適用を使って関数を返しているのでは?と思いながら検索していたらこのページにたどり着きました。おかげさまで一歩前進できそうです。
コメントを投稿