【お知らせ】プログラミング記事の投稿はQiitaに移行しました。

逆アセンブラ・インタプリタ

id:n7shi:20100701で開発した逆アセンブラインタプリタをF#に移植しました。Visual Studio 2008 ShellでF# Integrationを使用しています。

SilverlightではなくWindows Formsを使用しています。Silverlight ToolsでF#がサポートされたようですが、Visual Studio 2008 Shellからxapを出力する方法が分からなかったためです。以下のように手動で作成するしかなさそうです。

この方法だとUIデザインやデバッグが大変そうなので断念しました。

【追記】Visual Web Developer 2010 Express EditionでF#のライブラリをビルドする方法が判明しました。 ⇒ id:n7shi:20100710

テーブルの初期化

C#よりもF#の方が自然に書けると感じたのはテーブルの初期化です。

C#ではstaticコンストラクタでテーブルの初期化を行っていました。

public class Alpha
{
    private static string[] regname = new string[32];
    private static ulong[] mask = new ulong[256];

    static Alpha()
    {
        for (int i = 0; i < regname.Length; i++)
            regname[i] = ((Regs)i).ToString().ToLower();

        for (int i = 0; i < 256; i++)
        {
            ulong m = 0xff;
            for (int j = 1; j < 256; j <<= 1, m <<= 8)
                if ((i & j) != 0) mask[i] |= m;
        }
    }
}

F#では定義と同時に初期化コードが書けるため、コードが分断されずに自然だと思いました。以下はC#を直訳したコードです。

let regname =
    let regname = Array.zeroCreate<string> 32
    for i in 0..31 do regname.[i] <- enum<Regs>(i).ToString().ToLower()
    regname

let mask =
    let mask = Array.zeroCreate<uint64> 256
    for i in 0..255 do
        let mutable m = 0xffUL
        let mutable j = 1
        while j < 256 do
            if (i &&& j) <> 0 then mask.[i] <- mask.[i] ||| m
            j <- j <<< 1
            m <- m <<< 8
    mask

maskでのjとmに対する破壊的代入をやめてみます。

let mask =
    let mask = Array.zeroCreate<uint64> 256
    for i in 0..255 do
        for j in 0..7 do
            if (i &&& (1 <<< j)) <> 0 then mask.[i] <- mask.[i] ||| (0xffUL <<< (8 * j))
    mask

配列の要素に対する破壊的代入をやめるため内包表記に書き換えます。

let regname = [| for i in 0..31 -> enum<Regs>(i).ToString().ToLower() |]

let mask = let bittest i j = if (i &&& (1 <<< j)) = 0 then 0UL else 0xffUL <<< (8 * j)
           [| for i in 0..255 -> Seq.sum(seq { for j in 0..7 -> bittest i j }) |]

配列に対するnewが言語機能として用意されていないのに不満を感じていましたが、こういうことだったのかと思いました。

内包表記への書き換えはパズルのように感じました。C#の知識だけでは読み方が分からないので、慣れが必要だと思います。C#でもクエリ式を使えば同じようなことができます。

private static string[] regname = (from i in Enumerable.Range(0, 32)
                                   select ((Regs)i).ToString().ToLower()).ToArray();

private static ulong[] mask = (from i in Enumerable.Range(0, 256)
                               select (ulong)Enumerable.Range(0, 8).Sum(
                                   j => (i & (1 << j)) == 0 ? 0L : 0xffL << (8 * j))).ToArray();

C#でも積極的にこういう書き方をするべきかは悩みます。私は常用するまでには至りませんでした。