レンジ

コンパイラがforeachに遭遇した時、

foreach (element; range)
{
    // Loop body...
}

内部では下のものと同じように書き換えられます:

for (auto __rangeCopy = range;
     !__rangeCopy.empty;
     __rangeCopy.popFront())
 {
    auto element = __rangeCopy.front;
    // ループ本文...
}

下記のインターフェースを満たす任意のオブジェクトはレンジ(もしくはより明確にInputRange) と呼ばれ、これは反復処理のできる型です:

    interface InputRange(E)
    {
        bool empty();
        E front();
        void popFront();
    }

右の例を見てインプットレンジの実装と使用法をより詳しく調べてみましょう。

遅延評価

レンジは遅延評価をします。 必要になるまで評価は行われません。 そのため、無限レンジからレンジを得ることができます:

42.repeat.take(3).writeln; // [42, 42, 42]

値型と参照型

レンジオブジェクトが値型のとき、 レンジはコピーされてコピーだけが消費されます:

auto r = 5.iota;
r.drop(5).writeln; // []
r.writeln; // [0, 1, 2, 3, 4]

レンジオブジェクトが参照型 (たとえばclassstd.range.refRange) のとき、レンジは消費されて元に戻りません:

auto r = 5.iota;
auto r2 = refRange(&r);
r2.drop(5).writeln; // []
r2.writeln; // []

複製可能InputRangeForwardRange

標準ライブラリに存在するレンジの殆どは構造体であり、 foreachによる反復は、保証されないものの通常は非破壊的です。 それが保証されていることが重要なら、 InputRangeの特殊化であり.saveメソッドを持つフォワードレンジが利用できます:

interface ForwardRange(E) : InputRange!E
{
    typeof(this) save();
}
// 値 (構造体)
auto r = 5.iota;
auto r2 = refRange(&r);
r2.save.drop(5).writeln; // []
r2.writeln; // [0, 1, 2, 3, 4]

ForwardRangesはバイディレクショナルレンジとランダムアクセスレンジに拡張可能

複製可能なForwardRangeには2つの拡張があります。 1つ目はバイディレクショナルレンジ、2つ目はランダムアクセスレンジです。 バイディレクショナルレンジは後ろからの反復が可能です:

interface BidirectionalRange(E) : ForwardRange!E
{
     E back();
     void popBack();
}
5.iota.retro.writeln; // [4, 3, 2, 1, 0]

ランダムアクセスレンジはlengthを持ち、各要素に直接アクセスできます。

interface RandomAccessRange(E) : ForwardRange!E
{
     E opIndex(size_t i);
     size_t length();
}

Dの配列は最もよく知られたランダムアクセスレンジです:

auto r = [4, 5, 6];
r[1].writeln; // 5

遅延レンジアルゴリズム

std.rangestd.algorithm の関数はこのインターフェースを利用する素材を提供します。 レンジによって容易に反復操作のできるオブジェクトの背後に複雑なアルゴリズムの構築が可能となります。 さらに、レンジは反復の中でそれが本当に必要になったとき、 例えばレンジの次の要素がアクセスされるときにのみ計算を行う遅延オブジェクトを作ることができます。 後の応用セクションでは特殊なレンジアルゴリズムを紹介しましょう。

掘り下げる

rdmd playground.d