連想配列
連想配列は、必ずしも整数ではないインデックスを持つ、 を持ち、まばらに入力することができる。連想配列のインデックスは のインデックスはキーと呼ばれ、その型はKeyTypeと呼ばれる。
連想配列は、配列宣言の。 を配列宣言の[ ] :
int[string] aa; // 文字列キーでインデックス化されたintの連想配列 // 文字列キーでインデックス化される。 // KeyTypeはstringである。 aa["hello"] = 3; // キー"hello"に関連付けられた値を3に設定する int value = aa["hello"]; // キーから値を検索する assert(value == 3);
連想配列のキー型も要素型も、関数型や。 関数型やvoid 。
リテラル
auto aa = [21u: "he", 38: "ho", 2: "hi"]; static assert(is(typeof(aa) == string[uint])); assert(aa[2] == "hi");
連想配列リテラルを参照のこと。
キーを削除する
連想配列の特定のキーを削除するには remove 関数 "で削除できる:
aa.remove("hello");
remove(key) 与えられたキーが存在しない場合は何もしない。 を返す。 指定されたfalseキーが存在する場合は、それをAAから削除し、 を返す。 をAAから削除し、 を返す。 true
すべてのキーは、clear のメソッドを使って削除できる。
メンバーシップのテスト
InExpressionは、キーが連想配列内にあれば値へのポインタを返す。 null へのポインタを返す:
int* p; p = "hello" in aa; if (p !is null) { *p = 4; // キーに関連付けられた値を更新する assert(aa["hello"] == 4); }
クラスをKeyTypeとして使用する
クラスをKeyTypeとして使うことができる。そのためには をオーバーライドしなければならない。 Object をオーバーライドしなければならない:
- size_t toHash() @trusted nothrow
- bool opEquals(Object)
opEquals のパラメータは、定義されているクラスの型ではなく Object 型であり、定義されているクラスの型ではないことに注意。
例:」である:
class Foo { int a, b; override size_t toHash() { return a + b; } override bool opEquals(Object o) { Foo foo = cast(Foo) o; return foo && a == foo.a && b == foo.b; } }
- toHash が一貫して同じ値でなければならない。 opEquals が真を返すとき、一貫して同じ値でなければならない。言い換えれば、等しいとみなされる2つのオブジェクト は常に同じハッシュ値を持つべきである。 そうでなければ、未定義の動作が生じる。
- @safe 、@nogc 、pure 、const 、scope の属性をできるだけ使用する。 toHash opEquals をできるだけ使用すること。
構造体または共用体をKeyTypeとして使用する。
KeyTypeが構造体または共用体の場合、 デフォルトのメカニズムが使用される に基づいてハッシュとその比較を計算する。 のフィールドに基づいてハッシュとその比較を計算する。カスタムのメカニズムを使用することもできる。 以下の関数を構造体メンバとして提供することで、カスタムメカニズムを使用できる:
size_t toHash() const @safe pure nothrow; bool opEquals(ref const typeof(this) s) const @safe pure nothrow;
例えば:
import std.string; struct MyString { string str; size_t toHash() const @safe pure nothrow { size_t hash; foreach (char c; str) hash = (hash * 9) + c; return hash; } bool opEquals(ref const MyString s) const @safe pure nothrow { return std.string.cmp(this.str, s.str) == 0; } }
関数は@safe の代わりに@trusted を使うことができる。
- toHash が一貫して同じ値でなければならない。 opEquals が真を返すとき、一貫して同じ値でなければならない。言い換えると、等しいとみなされる2つの構造体 は常に同じハッシュ値を持つべきである。 そうでない場合、未定義の動作が生じる。
- @nogc 属性をできるだけ使用する。 toHash とopEquals のオーバーライドで使用する。
AAエントリーの設定に関する工事または割り当て
AAインデクシング・アクセスが代入演算子の左側にある場合、AAエントリーを設定するために特別に扱われる。 キーに関連するAAエントリを設定するために特別に扱われる。 キーを設定するために特別に扱われる。
string[int] aa; string s; //s = aa[1]; // 実行時にRangeErrorをスローする aa[1] = "hello"; // AAエントリを設定するために処理される s = aa[1]; // ルックアップに成功する assert(s == "hello");
割り当てられた値型がAA要素型と等価である場合:
- インデックス・キーがAAにまだ存在しない場合、新しいAAエントリが割り当てられ、割り当てられた値で初期化される。 割り当てられた値で初期化される。
- インデックス・キーがすでにAAに存在する場合、設定は通常の割り当てを実行する。 割り当てが実行される。
struct S { int val; void opAssign(S rhs) { this.val = rhs.val * 2; } } S[int] aa; aa[1] = S(10); // 最初の設定は、エントリaa[1]を初期化する assert(aa[1].val == 10); aa[1] = S(10); // 2番目の設定は通常の代入を呼び出し、 // 演算子オーバーロードはメンバopAssign関数に書き換える。 assert(aa[1].val == 20);
代入された値型がAA要素型と等価でない場合、式は通常のインデックスを持つ演算子のオーバーロードを呼び出すことができる。 型と等価でない場合、式は通常のインデクシングで演算子オーバーロードを呼び出すことができる。 にアクセスすることができる:
struct S { int val; void opAssign(int v) { this.val = v * 2; } } S[int] aa; aa[1] = 10; // is rewritten to: aa[1].opAssign(10)で、 // opAssignが呼び出される前にRangeErrorがスローされる
ただし、AA要素型が、代入値からの暗黙的コンストラクタ呼び出しをサポートする構造体である場合、暗黙的コンストラクションは実行されない。 ただし、AA要素の型が、代入された値からの暗黙のコンストラクタ呼び出しをサポー トする構造体である場合は、AAエントリの設定に暗黙の構築が使用される。 は AA エントリの設定に使用される:
struct S { int val; this(int v) { this.val = v; } void opAssign(int v) { this.val = v * 2; } } S s = 1; // OK、次のように書き換えられる: S s = S(1); s = 1; // OK、次のように書き換えられる: s.opAssign(1); S[int] aa; aa[1] = 10; // 最初の設定は次のように書き換えられる: aa[1] = S(10); assert(aa[1].val == 10); aa[1] = 10; // 2番目の設定は次のように書き換えられる: aa[1].opAssign(10); assert(aa[1].val == 20);
import std.bigint; BigInt[string] aa; aa["a"] = 10; // BigInt(10)を構築し、AAで移動する aa["a"] = 20; // aa["a"].opAssign(20)を呼び出す
存在しない場合は挿入する
AAアクセスで、キーに対応する値が必要な場合、値が存在しなければ挿入することはできない。 キーに対応する値がなければならない。この require 関数は、遅延引数によって新しい値を構築する手段を提供する。 引数を介して新しい値を構築する手段を提供する。遅延引数は、キーが存在しない場合に評価される。 に評価される。require 。 この操作により、複数のキー検索を行う必要がなくなる。
class C{} C[string] aa; auto a = aa.require("a", new C); // "a"を検索し、存在しなければ構築する
その値が作られたものなのか、すでに存在するものなのかを知る必要がある場合もある。 を知る必要があることがある。require "関数"は、値が構築されたかどうかを示すbooleanパラメータを提供しない。 パラメータは用意されていない。 関数やデリゲートを介して値を構築することができる。これにより、以下のように を使うことができる。
class C{} C[string] aa; bool constructed; auto a = aa.require("a", { constructed=true; return new C;}()); assert(constructed == true); C newc; auto b = aa.require("b", { newc = new C; return newc;}()); assert(b is newc);
高度な更新
通常、連想配列の値を更新するには、assign文を使う。 assign文で行う。
int[string] aa; aa["a"] = 3; // キー"a"に関連付けられた値を3に設定する
値がすでに存在するのか、それとも構築する必要があるのかによって、異なる操作を行う必要がある場合もある。 値がすでに存在するのか、それとも構築する必要があるのかによって、異なる操作を行う必要があることがある。そこで update 関数は、デリゲートを介して新しい値を構築する手段を提供する。 create この関数は、デリゲートを介して新しい値を構築する手段や、デリゲートを介して既存の値を更新する手段を提供する。update デリゲートを介して新しい値を構築したり、既存の値を更新したりする手段を提供する。update 。 キーのルックアップが不要になる。
int[string] aa; // create aa.update("key", () => 1, (int) {} // 実行されない ); assert(aa["key"] == 1); // refで値を更新する aa.update("key", () => 0, // 実行されない (ref int v) { v += 1; }); assert(aa["key"] == 2);
詳細は以下を参照のこと。 update.
イミュータブルAAのランタイム初期化
不変連想配列はしばしば望ましいが、時には実行時に初期化しなければならないこともある。 実行時に初期化しなければならないこともある。これには コンストラクタ(スコープによっては静的コンストラクタ)、 バッファ連想配列とassumeUnique :
immutable long[string] aa; shared static this() { import std.exception : assumeUnique; import std.conv : to; long[string] temp; // 変数バッファ foreach (i; 0 .. 10) { temp[to!string(i)] = i; } temp.rehash; // ルックアップを高速化する aa = assumeUnique(temp); } void main() { assert(aa["1"] == 1); assert(aa["5"] == 5); assert(aa["9"] == 9); }
構成と参照セマンティクス
連想配列のデフォルトはnull で、最初のキーと値のペアを代入するときに構築される。 を代入した時点で構築される。しかし、一度構築された連想配列は参照セマンティクスを持つ。 つまり、ある配列を別の配列に代入しても、データはコピーされない。これは、同じ配列に複数の参照を作成しようとする場合に特に重要である。 これは、同じ配列に複数の参照を作成しようとする場合に特に重要である。
int[int] aa; // デフォルトはnull int[int] aa2 = aa; // null参照をコピーする aa[1] = 1; assert(aa2.length == 0); // aa2はまだnullである aa2 = aa; aa2[2] = 2; assert(aa[2] == 2); // これで両方が同じインスタンスを参照する
プロパティ
連想配列のプロパティは以下の通りである:
Property | Description |
---|---|
.sizeof | 連想配列の参照サイズを返す。 32ビットでは4、64ビットでは8である。 |
.length | 連想配列の値の数を返す。 連想配列の値の数を返す。動的配列とは異なり、読み取り専用である。 |
.dup | 同じサイズの新しい連想配列を作成する。 を作成し、連想配列の内容をそこにコピーする。 |
.keys | 動的配列を返す。 その要素は連想配列のキーである。 |
.values | 連想配列の要素を値とする動的配列を返す。 を返す。 |
.rehash | 連想配列を再編成し、ルックアップがより効率的になるようにする。 rehash は、例えば以下のような場合に有効である、 プログラムはシンボル・テーブルのロードを終了し、次のような場合に有効である。 高速な検索が必要な場合などに有効である。再編成された配列への参照を返す。 |
.clear | 連想配列から残りのすべてのキーと値を削除する。 既存のストレージを再利用できるように、削除後の配列は再ハッシュされない。 これは、同じインスタンスへのすべての参照に影響する。destroy(aa) への現在の参照のみを設定するnull |
.byKey() | として使用するのに適した前方範囲を返す。 ForeachStatementの ForeachAggregateとして使用するのに適した前方範囲を返す。 のForeachAggregateとして使用するのに適した前方範囲を返す。 |
.byValue() | として使用するのに適した前方範囲を返す ForeachStatementの ForeachAggregateとして使用するのに適した前方範囲を返す。 として使用するのに適した前方範囲を返す。 |
.byKeyValue() | として使用するのに適した前方範囲を返す ForeachStatementの ForeachAggregateとして使用するのに適した前方範囲を返す。 へのForeachAggregateとして使用するのに適した前方範囲を返す。返されるペアは、 と 。 .key .value プロパティを持つ。 プロパティを持つ不透明な型で表される。 これは低レベルのインターフェイスであることに注意されたい。 これは連想配列の反復処理に対する低レベルのインターフェイスであり、連想配列と互換性がないことに注意してほしい。 とは互換性がない。 Tuple 型とは互換性がない。 Tuple との互換性のために、代わりに 代わりにstd.array.byPairを使う。 |
.get(Key key, lazy Value defVal) | key を検索し、存在すれば対応する値を返す。 さもなければdefVal を評価し返す。 |
.require(Key key, lazy Value value) | key を検索し、存在すれば対応する値を返す。 else はvalue を評価し、連想配列に追加して返す。 |
.update(Key key, Value delegate() create, Value delegate(Value) update) | key を調べる。もし存在すれば、update のデリゲートを適用する。 elseはcreate デリゲートを評価し、連想配列に追加する。 |
例:
連想配列の例:単語数
料理人が多すぎる
材料が多すぎる
import std.algorithm; import std.stdio; void main() { ulong[string] dictionary; ulong wordCount, lineCount, charCount; foreach (line; stdin.byLine(KeepTerminator.yes)) { charCount += line.length; foreach (word; splitter(line)) { wordCount += 1; if (auto count = word in dictionary) *count += 1; else dictionary[word.idup] = 1; } lineCount += 1; } writeln(" lines words bytes"); writefln("%8s%8s%8s", lineCount, wordCount, charCount); const char[37] hr = '-'; writeln(hr); foreach (word; sort(dictionary.keys)) { writefln("%3s %s", dictionary[word], word); } }
フルバージョンはwcを参照のこと。
連想配列の例:ペアを数える
連想配列は、foreach文を使ってキー/値で反復することができる。 foreach文を使う。例として 例 "として、文字列中の長さ2(別名2-mers)の部分文字列の出現回数を数える。 長さ2(別名2マース)の文字列の出現数がカウントされる:
import std.range : slide; import std.stdio : writefln; import std.utf : byCodeUnit; // UTF-8の自動デコードを回避する int[string] aa; // 文字列`arr`のアルファベットは限られている: {A、C、G、T} // したがって、パフォーマンスを向上させるために、デコードを行わずに反復を行うことができる auto arr = "AGATAGA".byCodeUnit; // 文字列内のすべてのペアを繰り返し、各ペアをカウントする // ('A', 'G'), ('G', 'A'), ('A', 'T'), ... foreach (window; arr.slide(2)) aa[window.source]++; // ソースはコード・ユニットのラップを解く // 連想配列のすべてのキーと値のペアを反復処理する foreach (key, value; aa) { writefln("key: %s, value: %d", key, value); }
> rdmd count.d key: AT, value: 1 key: GA, value: 2 key: TA, value: 1 key: AG, value: 2
DEEPL APIにより翻訳、ところどころ修正。
このページの最新版(英語)
このページの原文(英語)
翻訳時のdmdのバージョン: 2.108.0
ドキュメントのdmdのバージョン: 2.109.1
翻訳日付 :
HTML生成日時:
編集者: dokutoku