D言語の動的配列

私は大学院時代にずっとC++で開発をしていたのだが、その時に一番重宝したのはstd::vectorである。要素の追加・削除が簡単であるのに加え、at()関数を使えばメモリ境界のチェックもしてくれる。境界チェックに時間を割きたくなければ[]演算子で高速にアクセスすることも可能だ。おまけに、vectorが確保しているメモリを無理やり開放する方法まである(俗にswap技法と呼ばれる)。私の大学院での研究はかなり理論寄りで、自分のアイディアの正しさを証明するために、メモリ管理はそこそこシビアに行う必要があったので、これは重宝した。

D言語を始めた時にも、まず気になったのがC++vectorに対応するコンテナの存在だ。しかし、標準ライブラリを軽く探してみても、listや連想配列、赤黒木まで用意されているのに、vectorがなかった。それもそのはずで、DではC++vector的なコンテナの役割は、言語標準の動的配列が果たしているのだ。

D言語の配列は、主に静的配列と動的配列に分けられる。静的配列は個人的にワクワクしないので、今日は動的配列に話をフォーカスする。Dの動的配列は以下のように宣言する。

// 要素数100のT型の配列を宣言
auto hoge = new T[100];

ここで、Tは任意の型を表すとする。まず注目すべきポイントは、戻り値の型のautoだ。Dには型推論の機能が備わっており、この場合new T[100]が返す値の型はT[]型(Tの配列への参照)であると推論できるため、何も考えずにautoとしておけばよい。

型なんて自分できちっと書いてもそんなに労力は変わらないと思うかもしれない。しかし、私個人の感想としては、型名が非常に長かったり、標準ライブラリが定義しているあまり馴染みのない型の変数を宣言する場合などに役立つ。

話を配列に戻す。このようにして宣言された配列に対して、範囲外アクセスをしようとすると、必ずエラーを吐く。C言語では範囲外アクセスによってメモリを破壊し放題であるのに大して、これは随分と安全にできている。しかし、これだけだとC++でもJavaでも大差ない。Dの動的配列にはさらに隠された特殊能力がある。

まず簡単なところから言うと、Dの配列にはプロパティという概念がある。百聞は一見に如かずということで、コードを以下に示す。

// 動的配列hogeの長さを表示
writefln("The length of hoge is %s", hoge.length);
// hogeの長さを変える
hoge.length = 200;

ここではhogeが持つlengthというプロパティにアクセスしている。このように、Dの動的配列は自身の長さを知っており、その情報をいつでも読み出せる。また、lengthに数値を代入することで、配列の長さを変更することができる。

そして次こそがDの配列の真骨頂、スライシングである。pythonを使ったことがある人であれば、配列のスライスには馴染みがあるだろう。Dの配列にもこの機能があるのだ。例を以下に示す。

auto hoge = [100, 200, 300, 400];
// hogeの1番目の要素から$-1番目の要素をスライス
auto fuga = hoge[1 .. $];
// このassertは通る
assert(fuga == [200, 300, 400]);

ここで、$は配列の最後の要素の1つ先の要素を表す。つまり、最後の要素は$-1番目ということになる。この特殊な文字は'['と']'に囲まれた部分でのみ使うことができる。例では配列の1番目から最後の要素までをスライスしている。1 .. $のように範囲を指定すると、これは1から$-1番目までの要素を表す($番目は含まれない)。

このスライス機能は、これだけで十分すごい機能であるが、さらに驚くべきことに、Dは配列のスライスを生成するときに、配列の実体をコピーしない。単に、その範囲の前後を指す参照を返すだけだ。すなわち、この大変便利なスライス機能は、どれだけ多用しても頻繁なメモリコピーが発生しないのだ。ただし、配列の実体が1つしかないので、スライスされて得た配列の要素を書き換えると、当然元の配列の要素も書き換わる。Dの動的配列は基本的に参照セマンティクスで動作するのである。配列をコピーしたときなども同様の動作をする。

最後に1つだけ。Dでは、配列同士を結合することもできる。その時の演算子には注意が必要だ。

auto hoge = [100, 200, 300, 400];
auto fuga = hoge[1 .. $];
// 配列同士の結合演算子は'+'ではなく'~'
auto piyo = hoge ~ fuga;
assert(piyo == [100, 200, 300, 400, 200, 300, 400]);

ご覧の通り、演算子は'+'ではなく'~'になっている。これは演算子のあいまいさをなくすためという深淵なる理由によるものらしい。最初は気持ち悪く感じられるかもしれないが、すぐに慣れるだろう。

以上、D言語の動的配列についてざっと紹介した。一言で言えば、Dの動的配列は安全で便利だということである。また、速度面での心配がある人は、配列の範囲チェックをオフにすることもできる。記憶が定かではないが、メモリ管理もどうしてもやりたい人は手動で行うことができたはずだ。Dはプログラマに安全を提供しながらも、安全の檻に閉じ込めることは決してしない。

私が思うに、動的配列は言語の心臓部と言っても過言ではない。それがこれだけ使いやすくなっているだけでも、D言語を使う価値は十分にあるだろう。

参考:プログラミング言語D(詳細は以下)

プログラミング言語D

プログラミング言語D