D 1.0   D 2.0
About Japanese Translation

Last update Tue Nov 9 13:50:07 2010

連想配列

連想配列はindexが必ずしも整数である必要はなく、 疎なindexも扱えます。 連想配列のインデックスのことをkeyと呼び、その型をKeyTypeと呼びます。

連想配列は、 配列宣言の[]の中に KeyType を書いて宣言します:

int[char[]] b;		// 文字の配列で添え字づけられた
			// int型の連想配列 b
			// KeyTypeはchar[]
b["hello"] = 3;		// key "hello" に値 3 を関連付ける
func(b["hello"]);	// 3 を func() へ引数として渡す

連想配列のkeyは、 remove 関数によって取り除くことができます:

b.remove("hello");

InExpression によって、値がある連想配列のkeyであればその要素へのポインタ、 そうでなければ null が得られます:

int* p;
p = ("hello" in b);
if (p != null)
	...

関数型とvoidはKeyTypeにできません。

関数型とvoidは要素型にすることもできません。

クラスをKeyTypeとして使うには

クラスをKeyTypeとして使うこともできます。そのためには、 クラスの定義で、以下にあげる Object のメンバ関数をオーバーライドしてください:

hash_t は整数型へのaliasです

opCmpopEquals への引数の型はそのクラス自身ではなく、 Object型とすることに注意が必要です。

例:

class Foo
{
    int a, b;

    hash_t toHash() { return a + b; }

    int opEquals(Object o)
    {	Foo f = cast(Foo) o;
	return f && a == foo.a && b == foo.b;
    }

    int opCmp(Object o)
    {	Foo f = cast(Foo) o;
	if (!f)
	    return -1;
	if (a == foo.a)
	    return b - foo.b;
	return a - foo.a;
    }
}

処理系は opEqualsopCmp、あるいはその両方のいずれかを使用します。 実装する際は、オブジェクト同士が等しいかどうかの判定に関しては opEqualsopCmp の結果が矛盾しないよう注意してください。

構造体や共用体をKeyTypeとして使うには

KeyType が構造体型の場合、 構造体のバイナリ表現を使ったハッシュ計算や比較が デフォルトの方式として使われます。 カスタマイズしたい場合は、 以下の関数を構造体のメンバとして提供します:

 hash_t toHash();
int opEquals(KeyType* s);
int opCmp(KeyType* s);

例:

import std.string;

struct MyString
{
    string str;

    hash_t toHash()
    {   hash_t hash;
	foreach (char c; s)
	    hash = (hash * 9) + c;
	return hash;
    }

    bool opEquals(MyString* s)
    {
	return std.string.cmp(this.str, s.str) == 0;
    }

    int opCmp(MyString* s)
    {
	return std.string.cmp(this.str, s.str);
    }

}

処理系は opEqualsopCmp、あるいはその両方のいずれかを使用します。 実装する際は、オブジェクト同士が等しいかどうかの判定に関しては opEqualsopCmp の結果が矛盾しないよう注意してください。

プロパティ

連想配列のプロパティは:
連想配列のプロパティ
プロパティ 説明
.sizeof 連想配列への参照のサイズを返します。 典型的には、8 です。
.length 連想配列に格納された値の個数を返します。 動的配列とは異なり、読み取り専用です。
.keys 連想配列のkeyからなる 動的配列を返します。
.values 連想配列のvalueからなる 動的配列を返します。
.rehash 連想配列をin-placeで再構成し、より効率的なアクセスを可能にします。 rehashは、例えば、 プログラムがシンボルテーブルのロードを終えて、 さあ高速に参照したい、というときに効果があります。 返値は、再構成された配列自身です。

連想配列の例: 単語数カウント

import std.file;		// D のファイルI/O
import std.stdio;

int main (string[] args)
{
    int word_total;
    int line_total;
    int char_total;
    int[char[]] dictionary;

    writefln("   lines   words   bytes file");
    for (int i = 1; i < args.length; ++i)      // プログラム引数
    {
	char[] input;		// 入力バッファ
	int w_cnt, l_cnt, c_cnt;	// 単語, 行, 文字数
	int inword;
	int wstart;

	// input[] へファイルを読み込み
	input = cast(char[])std.file.read(args[i]);

	foreach (j, char c; input)
	{
	    if (c == '\n')
		    ++l_cnt;
	    if (c >= '0' && c <= '9')
	    {
	    }
	    else if (c >= 'a' && c <= 'z' ||
		    c >= 'A' && c <= 'Z')
	    {
		if (!inword)
		{
		    wstart = j;
		    inword = 1;
		    ++w_cnt;
		}
	    }
	    else if (inword)
	    {
		char[] word = input[wstart .. j];
		dictionary[word]++;        // 単語数カウントを増やす
		inword = 0;
	    }
	    ++c_cnt;
	}
	if (inword)
	{
	    char[] word = input[wstart .. input.length];
	    dictionary[word]++;
	}
	writefln("%8d%8d%8d %s", l_cnt, w_cnt, c_cnt, args[i]);
	line_total += l_cnt;
	word_total += w_cnt;
	char_total += c_cnt;
    }

    if (args.length > 2)
    {
	writef("-------------------------------------\n%8d%8d%8d total",
		line_total, word_total, char_total);
    }

    writefln("-------------------------------------");
    foreach (word; dictionary.keys.sort)
    {
	writefln("%3d %s", dictionary[word], word);
    }
    return 0;
}