writeln()に死す

* 以下のコードは、dmd2.067.1で動作を確認しました。
* D言語を使っていて気づいた、他愛のないことを書いています。
* __まあたらしい内容はありません__、ご注意ください

# __配列の全要素を表示する（ループ）__

配列の中身を表示するのを愚直にやると、
ブラケットで囲って、ループで回して、要素をカンマで区切って、
こんな感じでやると思います。

```D:D
import std.stdio;

// 一次元配列の表示関数
void writeArray(int[] arr)
{
    "[".write;
    foreach (elem; arr[0 .. $ - 1])
    {
        elem.write;
        ", ".write;
    }
    arr[$ - 1].write;
    "]".write;
}

// 二次元配列の表示関数
void writeMatrix(int[][] mat)
{
    "[".write;
    foreach (elem; mat[0 .. $ - 1])
    {
        elem.writeArray;
        ", ".write;
    }
    mat[$ - 1].writeArray;
    "]".write;
}

void main()
{
    auto arr = [0, 1, 2, 3, 4];
    auto mat = [[0, 1], [2, 3], [4, 5]];
    arr.writeArray;
    writeln("");
    mat.writeMatrix;
}
```

```:Output
[0, 1, 2, 3, 4]
[[0, 1], [2, 3], [4, 5]]
```

# __テンプレート化（再帰＋ループ）__
ここで`writeArray`と`writeMatrix`は、構造がほとんど同じなので
テンプレート関数を使って一つにまとめます。
`std.traits`の`isArray`テンプレートを使用して、
配列かそうでないかの場合分けをしています。

```D:D
import std.stdio, std.traits;

// 多次元配列の表示関数
void writeMatrix(M)(M mat)
{
    void writeArray(A)(A arr)
    {
        static if (isArray!A)
        {
            "[".write;
            foreach (elem; arr[0 .. $ - 1])
            {
                writeArray(elem);
                ", ".write;
            }
            writeArray(arr[$ - 1]);
            "]".write;
        }
        else
        {
            arr.write;
        }
    }
    writeArray(mat);
    writeln("");
}

void main()
{
    auto      arr  = [0, 1, 2, 3, 4];
    int[2][3] mats = [[0, 1], [2, 3], [4, 5]];
    auto      matd = [[0, 1], [2, 3], [4, 5]];
    arr.writeMatrix;
    mats.writeMatrix;
    matd.writeMatrix;
}
```

```:Output
[0, 1, 2, 3, 4]
[[0, 1], [2, 3], [4, 5]]
[[0, 1], [2, 3], [4, 5]]
```

次元を下げながら再帰をしていき、配列ではなくなったら
値を表示するという処理の流れです。
テンプレート化の効能として、多次元、及び静的配列にも対応することができました。
（ネスト関数をUFCSできないのが悔しいですね。。。）

# __文字列操作バージョン__

構造はずいぶんスッキリしましたが、
都度、標準出力に`write`しているのはイケてない気もします。
表示する内容の文字列をあらかじめ作成しておき、
最後に`writeln`するように変更してみましょう。

```D:D
import std.stdio, std.traits, std.conv;

void writeMatrix(M)(M mat)
{
    string result;
    void inner(A)(A arr)
    {
        static if (isArray!A)
        {
            result ~= "[";
            foreach (elem; arr[0 .. $ - 1])
            {
                inner(elem);
                result ~= ", ";
            }
            inner(arr[$ - 1]);
            result ~= "]";
        }
        else
        {
            result ~= arr.to!string;
        }
    }
    inner(mat);
    result.writeln;
}
```

# __やったか？！__

`std.string.join`で、ブラケットとカンマ区切りの加工を行い、
`std.string.map`で、ループ処理をすればもっと簡単になりそうです。

```D:D
import std.stdio, std.traits, std.conv, std.string, std.algorithm;

void writeMatrix(M)(M mat)
{
    string inner(A)(A arr)
    {
        static if (isArray!A)
            return ["[", "]"].join( arr.dup.map!( elem => inner(elem) ).join(", ") );
        else
            return arr.to!string;
    }
    inner(mat).writeln;
}
```

※`dup`がないと静的配列の場合にエラーになるようです。
なにはともあれ、よかったよかった。

# __あ、手がすべっｔｔ__

「あっ」

```D:D
import std.stdio;

void main()
{
    auto      arr  = [0, 1, 2, 3, 4];
    int[2][3] mats = [[0, 1], [2, 3], [4, 5]];
    auto      matd = [[0, 1], [2, 3], [4, 5]];

    arr.writeln;
    mats.writeln;
    matd.writeln;
}
```

```:Console
[0, 1, 2, 3, 4]
[[0, 1], [2, 3], [4, 5]]
[[0, 1], [2, 3], [4, 5]]
```

__「・・・」__

__「・・・」__

__「（普通にwritelnで出るじゃん・・・）」__


__※↓2015.07.27追記__
__コメントでwriteflnを使う方法を教えて頂きました。__

```D:D
auto      matd = [[0, 1], [2, 3], [4, 5]];
writefln("%(%(%d %)\n%)", matd);
```


```:Output
0 1
2 3
4 5
```

__「（writefln、かしこい・・・！）」__

# __結論__

__D言語の`writeln`関数は、多次元配列も表示してくれます。__
__とても便利！　皆さんどんどん使いましょう！__

# __次回、「to!(int[])に死す」。デュエルスタンバイ！（続きません）__