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++のテンプレート以上の事が出来そうです。