タプル
タプルは、単一のオブジェクトとして使用する複数の値を組み合わせるためのものだ。これらは、std.typecons
モジュールにあるTuple
テンプレートによってライブラリ機能として実装されている。
Tuple
一部の操作では、std.meta
モジュールにあるAliasSeq
を使用している。
この章では、タプルのより一般的な操作のみを説明する。タプルとテンプレートに関する詳細情報は、Philippe Sigaudの「D Templates: A Tutorial」を参照。
Tuple
およびtuple()
タプルは通常、便利な関数tuple()
によって構築される。
上記のtuple
の呼び出しは、int
の値42とstring
の値"hello"
で構成されるオブジェクトを構築する。プログラムの出力には、タプルオブジェクトとそのメンバーの型が含まれている。
Tuple!(int, string)(42, "hello")
上記のタプル型は、以下の擬似struct
定義と同等であり、おそらくまったく同じ方法で実装されている。
タプルのメンバーは通常、そのインデックス値によってアクセスされる。この構文は、タプルがさまざまな型の要素で構成される配列と見なせることを示唆している。
出力:
42
hello
メンバープロパティ
tuple
およびTuple
テンプレートがメンバー名でインスタンス化されている場合、プロパティによってメンバーにアクセスすることができる。以下の2つのメソッドは、同じ効果を持つ。
上記の定義により、.number
および.message
プロパティからもメンバーにアクセスできる。
出力:
インデックス0 | 42 |
---|---|
.number | 42 |
インデックス1 | hello |
.message | hello |
メンバーを値のリストとして展開
タプルメンバーは、関数呼び出し時の引数リストなどとして使用できる値のリストとして展開することができる。メンバーは、.expand
プロパティまたはスライスによって展開することができる。
上記のタプルは、int
、string
、double
、char
の4つの値で構成されている。これらの型は、foo()
のパラメータリストと一致するため、そのメンバーの展開をfoo()
の引数として使用することができる。bar()
を呼び出すと、タプルの最初のメンバーと最後の2つのメンバーから、一致する引数リストが作成される。
メンバーが同じ配列の要素として互換性がある限り、タプルの展開は配列リテラルの要素値としても使用できる。
上記の配列リテラルは、同じタプルを2回展開して初期化されている:
[1, 2, 3, 1, 2, 3]
コンパイル時foreach
値を展開できるため、タプルはforeach
文でも使用できる。
出力:
0 | 42 |
1 | hello |
2 | 1.5 |
上記のforeach
文は、実行時にループが実行されるような誤解を与えるかもしれない。しかし、そうではない。むしろ、タプルのメンバーに対して操作を行うforeach
文は、各メンバーについてループ本体を展開するものだ。上記のforeach
文は、次のコードと同等だ。
展開が行われる理由は、タプルのメンバーが異なる型の場合、foreach
体は型ごとに別々にコンパイルされなければならないためだ。
より強力なループ展開機能であるstatic foreach
については、後の章で説明する。
関数から複数の値を返す
タプルは、関数が1つの値しか返せないという制限の簡単な解決策となる。その例がstd.algorithm.findSplit
だ。findSplit()
は、別の範囲内の範囲を検索し、見つかった範囲の前、見つかった範囲、見つかった範囲の後の3つの部分で構成される結果を生成する。
出力:
前 | he |
---|---|
発見 | ll |
後 | o |
関数から複数の値を返すもう1つの方法は、struct
オブジェクトを返すことだ。
AliasSeq
AliasSeq
は、std.meta
モジュールで定義されている。これは、通常はコンパイラによって使用されるが、それ以外ではプログラマがエンティティとして利用できない概念を表すために使用される。値、型、およびシンボル(つまり、alias
テンプレート引数)をコンマで区切ったリストだ。以下は、そのようなリストの3つの例だ。
- 関数引数リスト
- テンプレート引数リスト
- 配列リテラル要素リスト
次の3行のコードは、同じ順序でこれらのリストを示した例だ。
Tuple
メンバーを展開する際、AliasSeq
を利用している。
AliasSeq
という名前は"alias sequence"から来ており、型、値、およびシンボルを含むことができる。(AliasSeq
およびstd.meta
は、以前はそれぞれTypeTuple
およびstd.typetuple
と呼ばれていた。)
この章では、型のみ、または値のみで構成されるAliasSeq
の例を紹介する。型と値の両方を使用した例については、次の章で説明する。AliasSeq
は、可変長のテンプレートで特に有用だ。これについても、次の章で説明する。
AliasSeq
値で構成される
AliasSeq
が表す値は、そのテンプレート引数として指定する。
3つのパラメータを取る関数を考えてみよう。
この関数は通常、3つの引数で呼び出される。
AliasSeq
は、これらの引数を1つのエンティティとして結合し、関数を呼び出すときに自動的に展開することができる。
この関数は1つの引数で呼び出されているように見えるが、上記のfoo()
の呼び出しは、前の呼び出しと同等だ。その結果、どちらの呼び出しも、同じ出力が生成される。
1 | hello | 2.5 |
また、arguments
は、auto
など、変数として定義されていないことにも注意。これは、特定のAliasSeq
インスタンスのalias
だ。AliasSeq
の変数を定義することも可能だが、この章の例では、それらをエイリアスとしてのみ使用する。
Tuple
で見たように、値が同じ配列の要素として互換性がある場合、AliasSeq
を使用して配列リテラルを初期化することもできる。
インデックス付けとスライシング
Tuple
と同じように、AliasSeq
の要素はインデックスとスライスでアクセスできる:
上記のAliasSeq
の最後の2つのメンバーと一致するパラメータを持つ関数があるとする。この関数は、AliasSeq
の最後の2つのメンバーだけのスライスを使用して呼び出すことができる。
AliasSeq
で構成される
AliasSeq
のメンバーは、型で構成することができる。つまり、特定の型の特定の値ではなく、int
自体のような型だ。型で構成されるAliasSeq
は、テンプレート引数を表すことができる。
2つのパラメータを持つstruct
テンプレートを持つAliasSeq
を使ってみよう。このテンプレートの最初のパラメータはメンバー配列の要素型を決定し、2番目のパラメータはメンバー関数の戻り値を決定する。
上記のコードでは、テンプレートが(double, int)
でインスタンス化されていることがわかる。AliasSeq
も同じ引数リストを表すことができる:
Types
は単一のテンプレート引数のように見えるが、自動的に展開され、テンプレートのインスタンス化は以前のようにS!(double, int)
になる。
AliasSeq
は、可変長のテンプレートで特に便利だ。この例については、次の章で説明する。
AliasSeq
を使用したforeach
Tuple
と同じで、AliasSeq
で動作するforeach
文は、実行時のループではない。むしろ、各メンバーに対してループ本体が展開される。
これを、上で定義した構造体S
に対して書いたユニットテストで例を見てみよう。次のコードは、要素型int
、long
、およびfloat
についてS
をテストする。この例では、ResultT
は常にsize_t
だ。
foreach
変数Type
は、int
、long
、float
の順に対応している。その結果、foreach
文は、以下のコードと同等のコードとしてコンパイルされる。
.tupleof
プロパティ
.tupleof
は、型またはオブジェクトのメンバーを表する。ユーザー定義型に適用すると、.tupleof
はその型のメンバー定義へのアクセスを提供する。
S.tupleof
は、プログラム内の2箇所に現れる。まず、要素の型は、typeof
を.tupleof
に適用して、各型がMemberType
変数として現れるように取得される。次に、メンバーの名前は、S.tupleof[i].stringof
によって取得される。
メンバー | 型 | 名前 |
---|---|---|
0 | int | number |
1 | string | message |
2 | double | value |
.tupleof
は、オブジェクトにも適用できる。その場合、オブジェクトのメンバーの値で構成されるタプルが生成される。
foreach
変数member
は、オブジェクトの各メンバーを表す:
メンバー | 型 | 値 |
---|---|---|
0 | int | 42 |
1 | string | hello |
2 | double | 1.5 |
ここで重要な点は、.tupleof
が返すタプルは、オブジェクトのコピーではなく、オブジェクト自体のメンバーで構成されていることだ。つまり、タプルのメンバーは、実際のオブジェクトのメンバーへの参照だ。
要約
tuple()
は、struct
オブジェクトと同様に、異なる型の値を結合する。- メンバーは、プロパティによってアクセスできる。
- メンバーは、
.expand
またはスライスによって値リストとして展開できる。 foreach
タプルとの組み合わせは実行時ループではなく、ループの展開だ。AliasSeq
関数引数リスト、テンプレート引数リスト、配列リテラル要素リストなどの概念を表す。AliasSeq
値と型で構成することができる。- タプルはインデックス付けとスライシングをサポートしている。
.tupleof
は、型およびオブジェクトのメンバーに関する情報を提供する。