Dのヴォルデモート型

この投稿では、D-ヴォルデモートタイプのユニークな機能について説明します。 使用できるが名前は付けられないタイプ。 この名前は彼らにはあまり適していませんが、ウォルター・ブライトは彼らをそう呼ぶのが大好きです。 ヴォルデモート型は、標準のフォボスライブラリ、特にstd.algorithmおよびstd.arrayモジュールで非常に一般的です。 マスタリングDは、スプリッターまたはジョイナーから返された型を検索するためのドキュメントを何時間も学習することができ、ヴォルデモート型を正確に返します。 この投稿の後、std.algorithmのソースを安全に開くことができます。You-Know-Whoを恐れることはないからです。



彼が一番



時には、既存の機能の相互作用が予期せぬ驚きにつながる可能性があります。 元々はヴォルデモートのタイプをDに置いていたと思いますが、実際にはAndrei Alexandrescuによって発見されました。 これらのVoldermortのタイプは何ですか? 読んでください。







まず、いくつかの背景情報。 素材を理解するには、 Rangesの概念が必要です。 最初の近似では、イテレータの代わりとして発明され、IEnumerableインターフェイスを使用したC#アプローチに非常に似ています。 Dでは、次のメソッドを実装する型はすべてInputRangeになります。

front - get the first element of the range popFront - remove the first element of the range empty - are there more elements in the range?
      
      







これらのメソッドは、型の反復の基礎を提供します。 楽しみのために、乱数の無限シーケンスを返すInputRangeを設計しましょう。 (このような関数ジェネレーターも呼び出します。)



このように見えるかもしれません(非常に良い乱数ジェネレーターではありませんが、すぐになります):

 module rnd; struct RandomNumberGenerator { this(uint seed) { next = seed; popFront(); // get it going } @property int front() { return ((next / 0x10000) * next) >> 16; } void popFront() { next = next * 1103515245 + 12345; } @property bool empty() { return false; } private: uint next; }
      
      







そしてそれを返す関数:

 RandomNumberGenerator generator(uint seed) { return RandomNumberGenerator(seed); }
      
      







そして、これらの数字のうち10個を印刷する素晴らしいプログラム:

 import std.stdio; import rnd; void main() { int count; foreach (n; generator(5)) { writeln(n); if (++count == 10) break; } }
      
      







これは通常すべてであり、停止します。 しかし、いくつかの迷惑な点があります。 理想的には、rnd.generator関数についてのみ知っている必要がありますが、モジュールにはRandomNumberGenerator型があり、これは単独で存在できます。 私のジェネレーターの抽象化からはみ出して、カプセル化違反のように見えます。



private属性でマークでき、rnd以外のモジュールはアクセスできませんでした。 しかし、このタイプはまだここにあり、属するゾーンの外側にあり、モジュールの他のメンバーはロックされているかどうかにかかわらずアクセスできます(Dのプライベート広告は同じモジュール内の他の広告から隠されません)。



さらに楽しいことに移りましょう。 まず、Dは宣言の型推論をサポートしているため、次のように記述できます。

 auto g = RandomNumberGenerator(seed);
      
      







gには、RandomNumberGenerator型が自動的に割り当てられます。 これは標準的なものです。 このスレッドをもう少し引き出して、関数から返される型を推測できます。

 auto square(double d) { return d * d; } auto x = square(2.3);
      
      







そして、コンパイラーは、square関数がdoubleを返すことを理解します。これは、戻り後の式のタイプだからです。 そしてもちろん、変数xもdouble型になります。 ここで、ジェネレーターの関数を次のように書き換えましょう。

 module rnd; auto generator(uint seed) { struct RandomNumberGenerator { @property int front() { return ((seed / 0x10000) * seed) >> 16; } void popFront() { seed = seed * 1103515245 + 12345; } @property bool empty() { return false; } } RandomNumberGenerator g; g.popFront(); // get it going return g; }
      
      







何か魅力的なことが起こりました。 RandomNumberGeneratorは、ジェネレーター関数のスコープ内にある型になりました。 関数の外からは見えません。 また、名前を付けることはできません-これはヴォルデモート型です。



このタイプのインスタンスのみを取得できます。

 auto g = generator(5);
      
      







そしてgを使い続けます。 私はあなたが考えていることを知っています-typeofを使用してRandomNumberGeneratorの別のインスタンスを作成します:

 auto g = generator(4); typeof(g) h;
      
      







申し訳ありませんが、これは機能しません。コンパイラは、その範囲外のVoldermort型の宣言を許可しません(技術的な理由は、ローカルシード変数へのアクセスがないためです)。



今、私を悩ます詳細は1つだけです。サイクルです。

 int count; foreach (n; generator(5)) { writeln(n); if (++count == 10) break; }
      
      







彼はとても古く見えます。 範囲を使用すると、太いループなしで実行できます。代わりにrange takeを使用して、この範囲の最初の10要素を取得します。

 void main() { foreach (n; take(generator(5), 10)) writeln(n); }
      
      







そして、 writelnを使用して、ループを完全に取り除きます。

 void main() { writeln(take(generator(5), 10)); }
      
      







ヴォルデモート型は実際に存在型のみですか?




タイプTが指定されており、Tから抽出でき、Tと特定の関係にあるタイプUは、存在タイプです。



たとえば、ポインターであるタイプTがある場合、次のようにして、そこから基本的な存在タイプUを推測できます。

 import std.stdio; void extractU(T)(T t) { static if (is(TU : U*)) writefln("type %s is a pointer to an %s", typeid(T), typeid(U)); else writefln("type %s is not a pointer", typeid(T)); } void main() { int* t; extractU(t); double d; extractU(d); }
      
      







表示されるもの:

 type int* is a pointer to an int type double is not a pointer
      
      







ヴォルデモート型は確実に実装を隠しますが、これは存在型になりません。 ヴォルデモート型は特定のユーバー型から派生できないため、存在しません。



おわりに


ヴォルデモート型はDで注目に値する発見となりました。これにより、型をカプセル化して使用できるようになりましたが、呼び出すことはできません。



ソース:ウォルター・ハイト・ヴォルデモートタイプD



All Articles