カリー化
高階関数のところで紹介しましたが、F#では関数を引数としたり、戻り値とすることができます。そこで、引数が複数ある関数を、「1つ目のパラメーターを引数とし、「2つ目以降のパラメーターを引数とする関数」を返す関数」にすることを「カリー化」と言います。
例えば、下の例はxとyをパラメーターとして受け取り、x+yを返す関数testaddの定義ですが、この関数は下記のようにカリー化できます。
//カリー前 let testadd x y = x + y ↓ //カリー化した後 let testadd = (fun x -> (fun y -> x + y) )
カリー化を用いずパラメーターを組(タプル)として扱う場合は、F#Interactiveからの戻りが下記のように異なります。
let addtesttest (x, y) = x + y ;;
▼
val addtesttest : int * int -> int //“int * int”の部分に注目
組を使用しない限り、F#では自動でカリー化が行われることが分かります。
let addtesttest x y = x + y ;;
▼
val addtesttest : int -> int -> int //“1つ目のint -> int”の部分に注目
カリー化を行うことで、オリジナルの関数は1引数(パラメーター)を受け取るラムダ式あるいは、ネストされた複数のラムダ式に変換されます。
カリー化のメリットとして、引数が1つになることから関数の合成が簡単になる傾向があることや、次に紹介する「部分適用」が可能になることなどがあげられます。
C#でのカリー化を下記に、参考まで示しておきます。
//カリー化する前 Func<int, int, int> f = (x, y) => x + y; //カリー化後 Func<int, Func<int, int>> f = x => y => x + y;
部分適用
カリー化を利用して、下記のように、「部分適用」と呼ばれる機能を使用することが可能になります。
このように、関数をカリー化して、1つめのパラメーター、もしくは、複数のパラメーター(全体で必要な引数より少ない数の引数)に定数値ををあらかじめバインドしてしまい、残りのパラメーターのみを受け取る中間関数のような関数を作成することを部分適用と言います。リスト20のように一定の値をインクリメントしたいときなどに使用すると便利な機能です。
//部分適用 //パラメーターを2つとる関数Addの定義 let Add x y = x + y ;; //パラメーターを1つだけ与えてみる Add 1 ;;
▼
val Add : int -> int -> int val it : (int -> int) = <fun:it@4> //int型のパラメータを受け取りint型の値を返す関数it(仮の名前)が返される。つまりit は (fun y -> 1 + y)である。
> it 10 ;;
▼
val it : int = 11
Addはxとyをパラメーターとし、x+yを返す関数です。このAddにパラメーターを1つだけ与えて実行してみると、関数Addは自動的にカリー化されて、(fun 1 -> (fun y -> 1 + y) )になります。このとき、このネストされたラムダ式にitという仮の名前が与えられ、返されます。itにパラメータとして、10を与えると 1+10が実行されます。
演算子
さて、ここまで、F#のいろいろな関数について解説してきましたが、F#では実は中置演算子も1つの関数です。中置演算子とは、引数に囲まれる形で配置され、前後の2つの引数から1つの結果を得る演算子のことです。例えば、"5+2=7"この式においては、"+"は中置演算子として機能しています。
F#Interactiveで下記のように実行すると、+という中置演算子がint型のパラメーターを2つ受け取りintを返す関数であることが分かります。
> (+) ;;
▼
val it : (int -> int -> int) = <fun:it@2>
これを利用して、下記のように関数を定義することも可能です。
> let Add = (+) ;;
▼
val Add : (int -> int -> int)
こんな風に独自の中置演算子を定義することもできます。
> let ( @-@/ ) a b = a*b*3+125 ;;
▼
val ( @-@/ ) : int -> int -> int
@-@/ は左右に置かれたパラメータ((aとb)に対して、a*b*3+125を実行する中置演算子です。実際に使用してみましょう。
> 10 @-@/ 4 ;;
▼
val it : int = 245
10*4*3+125の結果は、245なので、新たに定義した中置演算子が問題なく機能していることが分かります。
まとめ
今回はF#における関数型言語特有の機能を紹介しました。関数型言語独特のシンプルなF#のプログラミング方法に徐々に慣れていただければ、と思います。次回は「F#のオブジェクト指向プログラミング」を中心に、解説予定です。
