コンパイラが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]
レンジオブジェクトが参照型
(たとえばclass
やstd.range.refRange
)
のとき、レンジは消費されて元に戻りません:
auto r = 5.iota;
auto r2 = refRange(&r);
r2.drop(5).writeln; // []
r2.writeln; // []
InputRange
はForwardRange
標準ライブラリに存在するレンジの殆どは構造体であり、
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.range
や
std.algorithm
の関数はこのインターフェースを利用する素材を提供します。
レンジによって容易に反復操作のできるオブジェクトの背後に複雑なアルゴリズムの構築が可能となります。
さらに、レンジは反復の中でそれが本当に必要になったとき、
例えばレンジの次の要素がアクセスされるときにのみ計算を行う遅延オブジェクトを作ることができます。
後の応用セクションでは特殊なレンジアルゴリズムを紹介しましょう。