Tuple で遊ぶ
Ver-0.174 が出て、嬉々として遊んでいる人多いだろうな、と思いながらこれを書いている。
さて Tuple である。
std.typetuple には
template TypeTuple(TList...) { alias TList TypeTuple; }
というテンプレートがあるが、これは引数を型か値か特定していないので、値にも使える。
import std.typetuple; import std.stdio; import house.string; void main() { alias TypeTuple!(int, char[], double) Types; writefln(typeid(Types)); alias TypeTuple!(1, "AAA", 3.14) Values; writefln(typeid(typeof(Values))); writefln(toString(Values)); } ... (int,char[],double) (int,char[3],double) 1, AAA, 3.14
まぁ、製作者の意図としては、型タプルの製造に使って欲しいんだろうから、値のタプルを作るときには、
alias TypeTuple ValueTuple;
とでもやっておきべきなんでしょね。
あ、あと Tuple をいきなり toString() で文字列化しているがこれは、この日記でさんざん書いてきた汎用文字列化関数テンプレート使ってます。std.string とのバッティングがやで、メソッド名を頭大文字にしてきたが、このほど慣例にならって、toString() にしました。
まぁぶつかったときには、
alias house.string.toString stringOf;
とでもするとして。
あと、モジュール名も、house.tostring から 単なる house.string にしました。
さて、Tuple の初期化をばまずやってみるか。
Types values = Values; writefln("values = %s", toString(values)); ... values = 0, , nan
だめどすな。
Types v1 = (1, "AAA", 3.14); Types v2 = [1, "AAA", 3.14]; Types v3 = TypeList!(1, "AAA", 3.14); writefln(toString(v1)); writefln(toString(v2)); writefln(toString(v3)); ... 0, , nan 0, , nan 0, , nan
どれもコンパイルは通るが、いずれも意図した動作にならない。
ただ、変数宣言のあと、動的に代入するのはできた。
Types values; values[0] = Values[0]; values[1] = Values[1]; values[2] = Values[2]; writefln("values = %s", toString(values)); ... values = 1, AAA, 3.14
int[char[]] map; map["A"] = 0; map["B"] = 1; map["C"] = 2;
とやらねばならなかったのと、事情が似ている。
まぁ、そんなわけでレシピ集に書いた Map.Entry 作ったわけだが。
でも実質タプルリテラルは定義できてるのになぁ。
初期化がだめなら代入である。
なんとか1ステートメントで代入できないかと、以下のテンプレートを作成。
template CopyTuple(Values...) { void to(Types...)(inout Types lhs) { lhs[0] = Values[0]; static if (Values.length > 1) CopyTuple!(Values[1..$]).to(lhs[1..$]); } }
ちょっと複雑だが、これは2重のテンプレートね。
外側の CopyTuple を値でインスタンス化し、内側の to を型でインスタンス化する。
使い方は、
CopyTuple!(1, "AAA", 3.14).to(values);
といった具合。
しかし、
tuple.d(45): tuple Values is used as a type tuple.d(45): Error: can only slice tuple types, not void
なんでよ。
Values は値としてインスタンス化したはずなんすがねぇ。
ナニがご不満?
再帰がだめなので、羅列型にに逃げた。
template CopyTuple(Values...) { void to(Types...)(inout Types lhs) { static if (Values.length > 0) lhs[0] = Values[0]; static if (Values.length > 1) lhs[1] = Values[1]; static if (Values.length > 2) lhs[2] = Values[2]; static if (Values.length > 3) lhs[3] = Values[3]; static if (Values.length > 4) lhs[4] = Values[4]; static if (Values.length > 5) lhs[5] = Values[5]; static if (Values.length > 6) lhs[6] = Values[6]; static if (Values.length > 7) lhs[7] = Values[7]; static if (Values.length > 8) lhs[8] = Values[8]; static if (Values.length > 9) lhs[9] = Values[9]; } }
みっともねぇなぁ。つくづく羅列型はナサケない。(誰かできる人教えて下さい)
ともあれ、目的達成である。
Types values; CopyTuple!(1, "AAA", 3.14).to(values); writefln("values = %s", toString(values)); ... values = 1, AAA, 3.14
ところで Tuple を利用しようとしてすぐに思いつくのが構造体。
0.174 から構造体に tupleof プロパティがついたので。
toString() を型汎用、Tuple 対応に作ってあるので、構造体の toString() なんか以下のですんでしまう。
struct S { int id = 10; char[] name = "NAME"; char[] toString() { return "{ " ~ house.string.toString(this.tupleof) ~ " }"; } } writefln(S.init); ... { 10, NAME }
これは構造体のメンバ数、型、メンバ名に依存していないので、テンプレート化すべき。
template StructToString(S) { static assert(is (S == struct)); char[] toString() { return "{ " ~ house.string.toString(this.tupleof) ~ " }"; } } ... struct S { int id = 10; char[] name = "NAME"; mixin StructToString!(S); } writefln(S.init); ... { 10, NAME }
さらに、構造体の初期化はレシピ集の「構造体のコンストラクタ風」にさっきの CopyTuple が使える。
struct S { int id = 10; char[] name = "NAME"; mixin StructToString!(S); static S opCall(typeof(S.init.tupleof) args) { S s; CopyTuple!(args).to(s.tupleof); return s; } } writefln(S(3, "initialized")); ... { 3, initialized }
これまたテンプレート化すべき。
template StructInitializer(S) { static assert(is (S == struct)); static S opCall(typeof(S.init.tupleof) args) { S s; CopyTuple!(args).to(s.tupleof); return s; } } struct S { int id = 10; char[] name = "NAME"; mixin StructToString!(S); mixin StructInitializer!(S); } writefln(S(3, "initialized")); ... { 3, initialized }
もっともこれは、D言語が構造体は静的な初期化しかゆるしていないので、こんなことしてるわけだが。
配列も昔はそーだったけど、なんで静的な初期化しかできんのかねぇ?
ともあれ、メンバがどうだろうと構造体は初期化、文字列化が可能になってしまった。
念のため、いろいろ試してみた。
mixin Map!(char[], int); struct S { int id; char[] name; double age; creal[] points; int[char[]] map; mixin StructToString!(S); mixin StructInitializer!(S); } S s = S( 10, "Struct", 24.7, [ 0.0+1.2i, 1.1+2.3i, 2.2+3.4i ], Map(Entry("AAA", 1), Entry("BBB", 2), Entry("CCC", 3)) ); writefln(s); ... { 10, Struct, 24.7, [0+1.2i,1.1+2.3i,2.2+3.4i], [(AAA:1),(BBB:2),(CCC:3)] }
だいじょぶでおますな。
クラスの Tuple に関してはまた今度。