D言語の多次元配列コピーでの注意点

# はじめに
以前、[D言語の配列コピーでの注意点](https://qiita.com/devmynote/items/3d52f846922491301d0c)という記事を書きました。
１次元配列でのシャローコピー、ディープコピーについての記事になります。

今回は、多次元配列でのディープコピーについて、調べました。

# dupは１次元配列ではディープコピー、多次元配列ではシャローコピー
[dup](https://dlang.org/library/object/dup.html)は配列の要素をディープコピー（複製）する関数です。
ただ、多次元配列の場合、配列の参照先をディープコピーするだけになり、その先の配列の値まではディープコピーされません。
以下は、そのことがわかる例となります。

```d:sample1.d
import std.stdio;

void case1()
{
	int[] a = [ 1, 2, 3 ];
	int[] b;
	
	b = a.dup;
	writefln("a = %s", a);
	writefln("b = %s", b);
	
	b[1] = 4;
	writefln("a = %s", a);
	writefln("b = %s", b);
}

void case2()
{
	int[][] c = [[ 1, 2, 3 ], [ 4, 5, 6 ]];
	int[][] d;

	d = c.dup;
	writefln("c = %s", c);
	writefln("d = %s", d);

	d[1][1] = 8;
	writefln("c = %s", c);
	writefln("d = %s", d);
}

void main()
{
	case1();
	case2();
}
```
１次元配列（`a`と`b`）と２次元配列（`c`と`d`）で結果が異なることがわかります。

* `a`と`b`は異なる値になっている。（`a`の内容は、書き換わらない）
* `c`と`d`は同じ値になっている。（`c`と`d`は、実体は同じ配列を指している）

```txt:実行結果
a = [1, 2, 3]
b = [1, 2, 3]
a = [1, 2, 3]
b = [1, 4, 3]
c = [[1, 2, 3], [4, 5, 6]]
d = [[1, 2, 3], [4, 5, 6]]
c = [[1, 2, 3], [4, 8, 6]]
d = [[1, 2, 3], [4, 8, 6]]
```

# ベタな解決策
`sample2.d`のように、配列要素に対して１つずつ`dub`を使えば、２次元配列でもディープコピーできます。

```d:sample2.d
import std.stdio;

void main()
{
	// 2次元配列
	int[][] c = [[1, 2, 3], [4, 5, 6]];
	int[][] d = new int[][](2, 3);
	d[0] = c[0].dup;
	d[1] = c[1].dup;
	writefln("c = %s", c);
	writefln("d = %s", d);
	d[1][1] = 8;
	writefln("c = %s", c);
	writefln("d = %s", d);
}
```

* `c`と`d`は異なる値になっている。（`c`の内容は書き換わらない）

```txt:実行結果
c = [[1, 2, 3], [4, 5, 6]]
d = [[1, 2, 3], [4, 5, 6]]
c = [[1, 2, 3], [4, 5, 6]]
d = [[1, 2, 3], [4, 8, 6]]
```

# 多次元配列のディープコピー
多次元配列のディープコピーを行うための汎用的な関数が紹介されていました。（詳しくは、以下の参考情報）
以下にソースコードを紹介します。

```d:
import std.stdio;
import std.traits;

// This is for one-dimensional arrays
T mdup(T : E[], E)(T arr)
	if (!isArray!E)
{
	T result;
	static if (is (E == immutable)){
		// No need to copy immutable elements
		result = arr;
	} else {
		// Otherwise, we must make a copy
		result = arr.dup;
	}
	return result;
}

// This is for array of array types
T mdup(T : E[], E)(T arr)
	if(isArray!E)
{
	T result;
	static if (is (E == immutable)){
		// No need to go deeper than an immutable layer
		result = arr;
	} else {
		foreach(elem; arr) {
			result ~= mdup(elem);
		}
	}
	return result;
}
```

# 使用例
```d:sample3.d
import std.stdio;
import std.traits;

// This is for one-dimensional arrays
T mdup(T : E[], E)(T arr)
	if (!isArray!E)
{
	T result;
	static if (is (E == immutable)){
		// No need to copy immutable elements
		result = arr;
	} else {
		// Otherwise, we must make a copy
		result = arr.dup;
	}
	return result;
}

// This is for array of array types
T mdup(T : E[], E)(T arr)
	if(isArray!E)
{
	T result;
	static if (is (E == immutable)){
		// No need to go deeper than an immutable layer
		result = arr;
	} else {
		foreach(elem; arr) {
			result ~= mdup(elem);
		}
	}
	return result;
}

void main()
{
	// 2次元配列
	int[][] c = [[ 1, 2, 3 ], [ 4, 5, 6 ]];
	int[][] d = c.mdup;
	writefln("c = %s", c);
	writefln("d = %s", d);
	d[1][1] = 8;
	writefln("c = %s", c);
	writefln("d = %s", d);
}
```
```txt:実行結果
c = [[1, 2, 3], [4, 5, 6]]
d = [[1, 2, 3], [4, 5, 6]]
c = [[1, 2, 3], [4, 5, 6]]
d = [[1, 2, 3], [4, 8, 6]]
```

# 参考情報
D言語のフォーラムで話題になっていました。

https://forum.dlang.org/post/ko6vik$2rs3$1@digitalmars.com