最近、F# で遊んでいます。F# というのは Microsoft Research で研究されている ML 言語の亜種です。ML というのは最初期の関数型言語とされています。関数型言語というのは字面の定義では状態をもたない言語です。単純に言えば破壊的な代入などを持たないとされています。Wikipedia の関数型言語の項では
- APL
- CLOS
- ConcurrentClean
- F#
- Haskell
- LISP
- ML
- OCaml
- Scheme
- XSLT
が挙げられています。
便利な点としては、副作用が少ないため (すべての関数型言語がまったく副作用を持たないわけではない) 並列化という方向性とミックスしやすいのだと考えます。実際最近、話題になっている Erlang も並列化という観点からです。
とりあえず、テストとして階乗を計算してみました。
if (x <= 1) then 1 else (x * fact (x - 1));;
let answer = fact 5;;
let computeAnswer() = System.Console.WriteLine(answer)
let main = computeAnswer()
OCaml のサンプルコードを元にしてみました。これを末尾再帰に気をつけて手直しすると以下のようになります。
if (x <= 1) then r else (fact (x - 1) (r * x));;
let answer = fact 5 1;;
let computeAnswer() = System.Console.WriteLine(answer)
let main = computeAnswer()
これらのコードは OCaml プログラミング入門を参考にしました。
この 2 つのコードを IL レベルで比較してみます。
.method public static int32 fact(int32 x) cil managed
{
// コード サイズ 21 (0x15)
.maxstack 5
IL_0000: ldarg.0
IL_0001: ldc.i4.1
IL_0002: cgt
IL_0004: ldc.i4.1
IL_0005: xor
IL_0006: brfalse.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldarg.0
IL_000b: ldarg.0
IL_000c: ldc.i4.1
IL_000d: sub
IL_000e: call int32 Test::fact(int32)
IL_0013: mul
IL_0014: ret
} // end of method Test::fact .method public static int32 fact(int32 x,
int32 r) cil managed
{
// コード サイズ 22 (0x16)
.maxstack 5
IL_0000: ldarg.0
IL_0001: ldc.i4.1
IL_0002: cgt
IL_0004: ldc.i4.1
IL_0005: xor
IL_0006: brfalse.s IL_000a
IL_0008: ldarg.1
IL_0009: ret
IL_000a: ldarg.0
IL_000b: ldc.i4.1
IL_000c: sub
IL_000d: ldarg.1
IL_000e: ldarg.0
IL_000f: mul
IL_0010: starg.s r
IL_0012: starg.s x
IL_0014: br.s IL_0000
} // end of method Fact::factCode. 3 と Code. 4 を比較すると、Code. 4 がループになっているのに対し、Code. 3 は再帰呼び出しのままであることがわかります。F# でも末尾再帰な関数呼び出しにすることでより最適なコード生成が行われるようです。


コメントする