Macroの世界
Nemerleの強みの一つにMacroがあります。Cのマクロ同様(Pre)コンパイル時に手を出せるのですが、単なる置換だけでなく、解析木にアクセス出来ます。なので、属性を使った委譲構文でmixinモドキをやったり、LINQっぽいクエリ式を実現することも可能です。それだけ強力なら、ばんばん紹介すれば良いのですが、私があまり理解出来ていないため紹介出来ないのでした。(^^; それでも、最近、何となく分かってきてはいるのですが・・・
Fibonacci数を求めるマクロを作成してみます。
using System; using Nemerle.Collections; using Nemerle.Compiler.Parsetree; // fibonacci数を求める macro fibonacci(n : int) : PExpr { def fib (n) { | 0 => 0 | 1 => 1 | _ => fib (n - 1) + fib (n - 2) } <[ $(fib (n) : int) ]> } // fibonacci数の配列を求める macro fibonacci2(n : int) : PExpr { def fib (n, b, a, acc) { | (0, _, _, _) => acc; | _ => fib (n - 1, b + a, b, b :: acc); } // fibonacci数の配列 def nums = match (n) { | 0 => | 1 => [1] | _ => fib (n, 1, 0, ) } // Nemerleの構文式に変換する mutable result = []; foreach (n in nums) result ::= <[ $(n : int) ]>; <[ array [.. $result] ]> }
マクロは2つあり、fibonacciは値のみを、fibonacci2は配列を返します。では、早速、使ってみましょう。
using System; Console.WriteLine (fibonacci (10)); foreach (n in fibonacci2 (10)) Console.WriteLine (n); /* 結果 55 1 1 2 3 5 8 13 21 34 55 */
結果だけみると、何の変哲もないプログラムで何が嬉しいのか分かりませんね。ですが、これをreflectorを使って逆コンパイルしてみると・・・
private static void Main()
{
Console.WriteLine(0x37);
int numArray2 = new int { 1, 1, 2, 3, 5, 8, 13, 0x15, 0x22, 0x37 };
int[] numArray1 = numArray2;
int num1 = numArray1.Length;
for (int num2 = 0; ((num2 < num1) ? 1 : 0) == 1; num2++)
{
int num4 = numArray1[num2];
int num3 = num4;
Console.WriteLine(num3);
}
}
ごらんの通りアセンブリ(DLL/EXE)の中身は既に計算済みのfibonacci数です。Macroがコンパイル時に動作するというのは、こういう意味なのです。使い方によっては、C++のテンプレート以上の事が出来そうです。