D言語で九九表を表示

# Twitterで、九九表を表示するプログラム(Java)を見かけたのでD言語でもやってみた

# __0. まずは文字列をベタ書き__

```d:D
void multiplicationTable0()
{
    import std.stdio;

    "* | 1  2  3  4  5  6  7  8  9
-----------------------------
1 | 1  2  3  4  5  6  7  8  9
2 | 2  4  6  8 10 12 14 16 18
3 | 3  6  9 12 15 18 21 24 27
4 | 4  8 12 16 20 24 28 32 36
5 | 5 10 15 20 25 30 35 40 45
6 | 6 12 18 24 30 36 42 48 54
7 | 7 14 21 28 35 42 49 56 63
8 | 8 16 24 32 40 48 56 64 72
9 | 9 18 27 36 45 54 63 72 81"
    .writeln;
}
```

最速のコード
改行がそのまま表示されるのが何気に便利
学校の課題をこれで提出したら（たぶん）やり直し


# __1. foreachで繰り返す__

```d:D
void multiplicationTable1()
{
    import std.stdio;

    "* |%2s".writef(1);
    foreach (num; 2 .. 10)
        " %2s".writef(num);

    "\n-----------------------------".writeln;

    foreach (col; 1 .. 10) {
        "%s |%2s".writef(col, col);
        foreach (row; 2 .. 10)
            " %2s".writef(col * row);
        writeln;
    }
}
```

素直に繰り返しの部分を__`foreach`__にしたバージョン
__`writef`__(C言語の__`printf`__相当)の書式指定子は楽ちん

処理の流れをながめていると、大まかに3つの処理がある
（行見出しの表示、区切り線の表示、九九本体の表示）
ので、なんとなく分けて書いてみる


## __ロジックをわかりやすく書き直したい__

上の__`foreach`__版でもわかりやすいが、順番に並んでいる要素の最初や最後だけ
特別扱いをしてあげないといけない部分で、もやっとする

例えば行見出しの部分で、1の手前には空白が__1文字__、2～9の手前には空白が__2文字__
空いているのが気になってしまう

要は順番に並んでいる要素の、間にだけ空白を入れたいだけなのだ


# __2. 標準ライブラリを使用する__

こういう一般的な悩み(？)は、標準ライブラリで何とかなる場合が多い
__`std.string`__の__`join`__がそのままの機能なので、
次のように書き換えてみた

```d:D
void multiplicationTable2()
{
    import std.stdio;
    import std.string    : format, join;
    import std.range     : iota, repeat, tee, No;
    import std.algorithm : map, each;

    (
        "* |" ~                   // * |
        iota(1, 10)               //  1  2  3  4  5  6  7  8  9
            .map!(num => num.format!"%2s")
            .join(" ")

    ).writeln;

    "-".repeat(29).join.writeln;  // -----------------------------

    iota(1, 10)                   // # |## ## ## ## ## ## ## ## ##
        .tee!(col => "%s |".writef(col), No.pipeOnPop)
        .each!(col => iota(1, 10)
            .map!(row => (col * row).format!"%2s")
            .join(" ")
            .writeln);
}
```

ついでに__`foreach`__を__`iota`__＋__`map`__/__`each`__、__`tee`__で置き換え
意味もなく__`repeat`__なども使用している


# __3. 標準出力への書き込み回数を減らす__

ここまできたら__`write`__を一回だけにしたい
1つの大きな文字列を作ってもいいが、ごちゃごちゃしそうなので
まずは、これまでの3つのまとまりをそれぞれ要素とする配列を作成して
最後にすべての要素を改行で__`join`__する方向でやってみよう
（ついでにマジックナンバーを少し整理）

```d:D
void multiplicationTable3()
{
    import std.stdio;
    import std.conv      : to;
    import std.string    : format, join;
    import std.range     : iota, repeat;
    import std.algorithm : map;

    enum E = 9;
    enum S = E * 3 + 1 + 1;
    [
        "* |" ~              // * |
        iota(1, E + 1)       //  1  2  3  4  5  6  7  8  9
            .map!(num => num.format!"%2s")
            .join(" "),

        "-".repeat(S).join,  // -----------------------------

        iota(1, E + 1)       // # |## ## ## ## ## ## ## ## ##
            .map!(col => col.to!string ~ " |" ~
                iota(1, E + 1)
                    .map!(row => (col * row).format!"%2s")
                    .join(" "))
            .join("\n")

    ].join("\n").writeln;
}
```


## __表の大きさを変更できるようにしたい……が__

9x9以外にも20x20なども表示できるようにするにはどうしよう
九九の表そのものは、実行時に引数で大きさを渡せば何とでもなりそうだが
問題は__`format`__の書式指定子の部分がコンパイル時に決まることだ。

自前で必要な表示桁数を計算して、空白を補ってもいいが
ここはD言語らしくコンパイル時に解決する方向で考えよう


# __4. テンプレート引数__
D言語のテンプレート関数には、型名の他に文字列などコンパイル時に決まるものを
__テンプレート引数__として渡すことができる
これを使って、書式指定子の__%2s__を__%3s__、__%4s__などに書き換える

```d:D
void multiplicationTable4(const int E = 9)()
{
    import std.stdio;
    import std.conv      : to;
    import std.string    : format, join;
    import std.range     : iota, repeat;
    import std.algorithm : map;

    enum R = E.to!string.length.to!string;         // 行見出しの文字数
    enum C = (E ^^ 2).to!string.length.to!string;  // 列見出しの文字数
    enum S = E * (C.to!int + 1) + R.to!int + 1;    // 区切り線の長さ
    [
        "*".format!("%" ~ R ~ "s |") ~         // * |
        iota(1, E + 1)                         //  1  2  3  4  5  6  7  8  9
            .map!(num => num.format!("%" ~ C ~ "s"))
            .join(" "),

        "-".repeat(S).join,                    // -----------------------------

        iota(1, E + 1)                         // # |## ## ## ## ## ## ## ## ##
            .map!(col => col.format!("%" ~ R ~ "s |") ~
                iota(1, E + 1)
                  .map!(row => (col * row).format!("%" ~ C ~ "s"))
                  .join(" "))
            .join("\n")

    ].join("\n").writeln;
}
```

この関数を呼び出すときは
__`関数名!(テンプレート引数)(実行時引数)`__
という形式で呼び出すことになる
※実行時引数がない場合、`()`は省略できる

```d:D
void main()
{
    multiplicationTable4();       // 9x9
    multiplicationTable4!(5)();   // 5x5
    multiplicationTable4!(20);    // 20x20
    multiplicationTable4!39;      // 39x39
}
```


# __5. CTFE__

よく考えたらコンパイル時に表示できそう

```d:D
auto multiplicationTable5(const int E = 9)()
{
    // 前略
    return
    [
        "*".format!("%" ~ R ~ "s |") ~         // * |
    // 中略
    ].join("\n");
}
```

```d:D
pragma(msg, multiplicationTable5); // コンパイル時

void main()
{
    import std.stdio;
    multiplicationTable5.writeln;  // 実行時
}
```


# __おわり__

format部分は、テンプレートmixinにした方が見やすいかも
D言語で書けたのでとりあえず満足