F#入門(8)関数の部分適用
カリー化(currying)とは、n個の引数を取る1つの関数を、1つの引数を取るn個の関数の連鎖に変換する操作のことです。
数式を使って書くと、関数
f:(X1×X2×……Xn)→Y
が与えられた時、カリー化関数(curried function)
curry(f):X1→(X2→(……→Xn……))
を得る操作をカリー化と言います。この場合、カリー化関数curry(f)
は1つの引数x1∈X1
を取り関数X2→(X3→(……→Xn……))
を返す関数です。返された関数X2→(X3→(……→Xn……))
は1つの引数x2∈X2
を取り関数X3→(X4→(……→Xn……))
を返す関数です。このような連鎖がn
回続くことになります。
F#の関数は通常自動的にカリー化されます。
例えば、2つの引数を取ってその和を返す関数は次のようになります。
let summation1 a b = a + b
これは記法上2個の引数を取る関数に見えますが、F#では自動的にカリー化されるので、以下のような関数を返す関数と同じになります。
let summation2 a =
fun b -> a + b
実際、2つの関数をF#インタープリタで定義してみると全く同じ結果が返ってきます。
また、これは括弧がない点を除いて、上記の数式によるカリー化関数の表現とも同じであることが分かります。
関数summation1
で第1引数だけを渡せば、第1引数が部分適用された(partially applied)関数が返されます。
let x = 4
let y1 = 6
let y2 = 8
let summation1 a b = a + b
let summation2 = summation1 x
let result1 = summation2 y1
let result2 = summation2 y2
printfn "%d + %d = %d" x y1 result1
printfn "%d + %d = %d" x y2 result2
F#の関数は全て自動的にカリー化されますが、場合によってはカリー化を抑止したいことがあります。つまり、関数を複数個の引数を取る1つの関数として定義したい場合があります。
その場合は、引数全体を括弧で囲みそれぞれの引数をカンマで区切ります。そうすると自動的にカリー化されることはありません。
例えば、先の関数summation1
を自動的にカリー化させないようにするには、次のようにします。
let summation (a, b) = a + b
関数の定義後にF#インタープリタによって返された結果が先程とは異なっており、int * int -> int
と2つのint
型の引数を取ってint
型の値を返す関数が定義されたことが分かります。
この関数を呼び出すときは、2つの引数を同時に渡さなければならないのは勿論ですが、関数を定義した時と同じように引数全体を括弧で囲みそれぞれの引数をカンマで区切らなければなりません。さもないと、エラーが発生します。具体的には以下のように書きます。
printfn "result = %d" (summation (1, 2))
引数全体を括弧で囲みそれぞれの引数をカンマで区切らないと、summation 1
は関数ではなく、引数を適用できないと怒られます。
1つしか引数を渡さないと、型が違うと怒られます。
