D 2.0
About Japanese Translation

www.digitalmars.com
Last update Sat Mar 6 18:01:34 2010

std.range

このモジュールは、レンジの有用な操作を定義しています。 このモジュールの構成のアイデアは Leonardo Maffi によるものです。

License:
Boost License 1.0.

Authors:
Andrei Alexandrescu

template isInputRange(R)
R が入力レンジ(Input Range)のとき true を返します。 入力レンジは基本操作 emptypopFrontfront を提供する必要があります。 以下のコードはどんな入力レンジに対してもコンパイルが通ります。

R r;             // レンジオブジェクトを定義できる
if (r.empty) {}  // 空かどうかテストできる
r.popFront;          // 次の要素へ進めることができる
auto h = r.front; // レンジの先頭要素を取り出せる
入力レンジの意味論 (コンパイル時チェックできない性質) としては、 以下を満たすものとします (r の型が R であるとして):

  • r.empty は、レンジにまだ値が残っているとき、そのときに限り false を返す
  • r.front は、 レンジの現在の先頭要素を返す。値返しまたは参照返しのどちらでもよい。r.front は、仮に r.empty を呼び出したら false を返す状況でのみ使用できる
  • r.popFront は、レンジの指す先頭を一つ次の要素に進める。 r.popFront の呼び出しは、仮に r.empty を呼び出すと false を返す状況でのみ使用できる

template isOutputRange(R,E)
R が出力レンジ(Output Range)のとき true を返します。 出力レンジは基本操作 put として E 型のオブジェクトを受け取る関数を提供する必要があります。以下のコードはどんな出力レンジに対してもコンパイルが通ります。

R r;             // レンジオブジェクトを定義できる
E e;
r.put(e);        // 要素をレンジに書き込める
出力レンジの意味論 (コンパイル時チェックできない性質) としては、 以下を満たすものとします (r の型が R であるとして):

  • r.put(e)e をレンジに (レンジ毎のやり方で) 書き込み、レンジの先頭を一つ先にすすめます。 r.put の呼び出しを繰り返すと、レンジに要素が次々追加されていきます。put は書き込み失敗を示す例外を投げることがあります。

template isForwardRange(R)
R が前進レンジ(Forward Range)のとき true を返します。 前進レンジは、入力レンジの機能に加え、単純にコピーすることで同じ型のレンジの "チェックポイント" を作ることができるものです。 入力レンジだけれど前進レンジではないよくある例としては、ファイルやソケットを読み取りレンジがあります; これらのレンジをコピーしてもストリーム中の位置を保存せず、 ほとんどの場合、ストリーム全体をメモリに置かないための内部バッファを再利用します。 結果として、コピー元とコピー先のどちらのストリームを進めても、 もう一方に影響してしまいます。 以下のコードはどんな前進レンジに対してもコンパイルが通ります。

static assert(isInputRange!(R));
R r1;
R r2 = r1;           // レンジを他へコピーできる
前進レンジの意味論 (コンパイル時チェックできない性質) は、 入力レンジのそれに加え、 レンジオブジェクトのコピーを取ることで バックトラックが可能であることが要求されます。

template isBidirectionalRange(R)
R が双方向レンジ(Bidirectional Range)のとき true を返します。 双方向レンジは、前進レンジの機能に加え基本操作 backpopBack を提供する必要があります。 以下のコードはどんな双方向レンジに対してもコンパイルが通ります。

R r;
static assert(isForwardRange!(R)); // 前進レンジである
r.popBack;                        // レンジの終端側を一つ縮めることができる
auto t = r.back;                   // レンジの最後の要素を取得できる
双方向レンジの意味論 (コンパイル時チェックできない性質) としては、 以下を満たすものとします (r の型が R であるとして):

  • r.back がレンジの最後の要素を(値もしくは参照で)返す。 r.back は、仮に r.empty を呼び出したら false を返す状況でのみ使用できる

template isRandomAccessRange(R)
R がランダムアクセスレンジ(Random Access Range)のとき true を返します。 ランダムアクセスレンジは、双方向レンジの機能に加えて基本関数opIndexを提供する有限レンジか、あるいは前進レンジの機能に加えて基本関数opIndexを提供する無限レンジです。 以下のコードはどんなランダムアクセスレンジに対してもコンパイルが通ります。

R r;
static assert(isForwardRange!(R)); // 前進レンジである
static assert(isBidirectionalRange!(R) || isInfinite!(R));
                                  // 双方向または無限
auto e = r[1];                    // 添え字アクセス可能
ランダムアクセスレンジの意味論 (コンパイル時チェックできない性質) としては、 以下を満たすものとします (r の型が R であるとして):
  • r.opIndex(n) がレンジの n番目の要素への参照を返す

template ElementType(R)
R の要素の型。R は必ずしもレンジでなくとも良い。 要素の型は、型 R のオブジェクト r に対する r.front の結果で決まります。例えば、ElementType!(T[])T です。

template hasSwappableElements(R)
R が前進レンジでswap可能な要素型を持つときに true を返します。 以下のコードはどんなランダムアクセスレンジに対してもコンパイルが通ります。

R r;
static assert(isForwardRange!(R));  // 前進レンジである
swap(r.front, r.front);              // レンジの要素をswap可能

template hasAssignableElements(R)
R が前進レンジで代入可能な要素型を持つときに true を返します。 以下のコードはどんなランダムアクセスレンジに対してもコンパイルが通ります。

R r;
static assert(isForwardRange!(R));  // 前進レンジである
auto e = r.front;
r.front = e;                         // 要素に代入が可能

template hasLength(R)
R が、整数型の値を返す length メンバを持っているときに true を返します。 R は必ずしもレンジでなくても構いません。注意点として、length はオプション的な基本操作で、どのレンジもこれを実装することは必須ではありません。 レンジによっては明示的に長さを保持しない実装もありえますし、 実際にレンジの端まで行かなければ終端のわからないレンジ (ソケットストリーム等) もあり、 さらに無限の長さをもつレンジもあり得ます。

template isInfinite(Range)
無限入力レンジに対して true を返します。 無限入力レンジは、 静的に定義された常に false となる empty メンバを持ちます。 例えば:

struct InfiniteRange
{
    enum bool empty = false;
    ...
}

template hasSlicing(Range)
Range が整数を指定するスライス演算子を持ち、 入力レンジを返す時に、true となります。 以下のコードは hasSlicingtrue の時にコンパイルが通ります:

Range r;
auto s = r[1 .. 2];
static assert(isInputRange!(typeof(s)));

size_t walkLength(Range)(Range range, size_t upTo = size_t.max);
任意のレンジに適用できる、 ベストエフォート型の length の実装です。

hasLength!(Range) が true ならば、単に range.length を返し、upTo は無視します。

そうでなければ、レンジを先頭から順に読んでいき、 見つかった要素数を返します。Ο(n) 回の range.emptyrange.popFront の呼び出し(ただし nrange の実際の長さ)を行います。upTo パラメタは、 レンジの長さが最低いくつか以上であるかどうかだけに興味があるようなケースに役立ちます。 パラメタ upTo が指定されていれば、upTo ステップ進んだ時点で計算を止め、upTo を返します。

struct Retro(R) if (isBidirectionalRange!(R) && !isRetro!(R));
Retro!(R) retro(R)(R input);
双方向レンジを逆向きにたどります。

Example:
int[] a = [ 1, 2, 3, 4, 5 ];
assert(equal(retro(a) == [ 5, 4, 3, 2, 1 ][]));

bool empty();
input.empty へと転送されます

void popFront();
input.popBack へと転送されます

void popBack();
input.popFront へと転送されます

ElementType!(R) opIndex(uint n);
input[input.length - n + 1] へと転送されます。R がランダムアクセスレンジで RR.length がある場合に限り定義されます。

size_t length();
レンジの長さを返す基本操作です。 input.length に転送されるため、hasLength!(R) が真なときにのみ定義されます。

struct Stride(R) if (isInputRange!(R));
Stride!(R) stride(R)(R input, size_t n);
レンジ r をストライド n でアクセスします。 ランダムアクセスレンジならば、添え字アクセスによってレンジ上を動きます。 そうでなければ、popFront を必要な回数呼び出して動作します。

Example:
int[] a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ];
assert(equal(stride(a, 3) == [ 1, 4, 7, 10 ][]));

this(R input, size_t n);
ストライドを初期化

Stride opSlice();
this を返します。

bool empty();
input.empty へと転送されます。

void popFront();
@@@

void popBack();
@@@

ElementType!(R) front();
input.front へと転送されます。

ElementType!(R) back();
余分な要素を除いた後 input.back へと転送されます。

ElementType!(R) opIndex(uint n);
input[input.length - n + 1] へと転送されます。R がランダムアクセスレンジで RR.length があるときに限り定義されます。

size_t length();
レンジの長さを返す基本関数です。 input.length に転送されるため、 hasLength!(R) が真のときに限り定義されます。

template Chain(R...) if (allSatisfy!(isInputRange,R))
Chain!(R) chain(R...)(R input);
複数のレンジを順にたどるレンジです。関数 chain は任意の個数のレンジを引数にとり、Chain!(R1, R2,...) オブジェクトを返します。 レンジ自体の型は異なっていても構いませんが、要素型は統一されている必要があります。 結果は front, popFront, empty を提供するレンジになります。 入力が全て length を持つランダムアクセスレンジならば、 Chain その二つの機能も同時に提供します。

レンジが1つだけ Chainchain に渡された場合 Chain 型はそのレンジへのaliasとなります。

Example:
int[] arr1 = [ 1, 2, 3, 4 ];
int[] arr2 = [ 5, 6 ];
int[] arr3 = [ 7 ];
auto s = chain(arr1, arr2, arr3);
assert(s.length == 7);
assert(s[5] == 6);
assert(equal(s, [1, 2, 3, 4, 5, 6, 7][]));

struct Radial(R) if (isRandomAccessRange!(R) && hasLength!(R));
Radial!(R) radial(R)(R r);
Radial!(R) radial(R)(R r, size_t startingIndex);
ランダムアクセスレンジを、 指定されたインデックスから始めて左右に広がりながら順にアクセスします。 始点を与えなかった場合、 レンジのちょうど中央から開始します。元のレンジ全体を列挙し終わると終了します。

Example:
int[] a = [ 1, 2, 3, 4, 5 ];
assert(equal(radial(a) == [ 3, 2, 4, 1, 5 ][]));
a = [ 1, 2, 3, 4 ];
assert(equal(radial(a) == [ 2, 3, 1, 4 ][]));

this(R input);
レンジを受け取って中央からの列挙を開始します。 長さが偶数のレンジの場合は、中心のすぐ左の要素から列挙が始まります。 二番目は、一番目のすぐ右の要素です。 このコンストラクタを簡単に呼び出すには、 補助関数 radial(input) を使用します。

this(R input, size_t startingPoint);
レンジを受け取って input[mid] からの列挙を開始します。 二番目は、 一番目のすぐ右の要素です。input[mid] の右に要素がない場合は、input[mid - 1] から降順に列挙が行われます。このコンストラクタを簡単に呼び出すには、 補助関数 radial(input,startingPoint) を使用します。

Radial opSlice();
this を返します。

bool empty();
全ての要素を列挙し終わっていれば true を返す、 レンジの基本操作です

void popFront();
レンジを一つ次の要素に進める基本操作です

ElementType!(R) front();
現在指している要素を返すレンジの基本操作です。 emptyな場合は例外を投げます。


Take!(R) take(R)(R input, size_t n);
Take!(R) take(R)(size_t n, R input);
レンジから(遅延評価で)最大 n 駒での要素を取り出します。 特に、無限レンジを扱うときに有用です。 入力が ランダムアクセスレンジで length がサポートされていた場合、Take もそれらの機能を同様にサポートします。

Example:
int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
auto s = take(arr1, 5);
assert(s.length == 5);
assert(s[4] == 5);
assert(equal(s, [ 1, 2, 3, 4, 5 ][]));

size_t popFrontN(Range)(ref Range r, size_t n);
レンジ r 自身 (コピーではない) を、nr.popFront を呼び出して先に進めます。rpopFrontN に参照渡しされるので、元々のレンジが影響されます。スライス操作をサポートするレンジならば Ο(1) ステップ、それ以外では Ο(n) ステップの時間がかかります。

Example:
int[] a = [ 1, 2, 3, 4, 5 ];
a.popFrontN(2);
assert(a == [ 3, 4, 5 ]);

size_t popBackN(Range)(ref Range r, size_t n);
レンジ r 自身 (コピーではない) を、nr.popBack を呼び出して終端を縮めます。 rpopBackN に参照渡しされるので、元々のレンジが影響されます。 スライス操作をサポートするレンジならば Ο(1) ステップ、それ以外では Ο(n) ステップの時間がかかります。

Example:
int[] a = [ 1, 2, 3, 4, 5 ];
a.popBackN(2);
assert(a == [ 1, 2, 3 ]);

struct Repeat(T);
Repeat!(T) repeat(T)(T value);
一つの値を永久に繰り返します。例:
enforce(equal(take(repeat(5), 4), [ 5, 5, 5, 5 ][]));

T front();
T back();
bool empty;
void popFront();
T opIndex(uint);
レンジの基本操作

Take!(Repeat!(T)) replicate(T)(T value, size_t n);
value をちょうど n 回繰り返します。take(repeat(value), n) と同等です。

struct Cycle(R) if (isForwardRange!(R));
struct Cycle(R) if (isStaticArray!(R));
Cycle!(R) cycle(R)(R input);
Cycle!(R) cycle(R)(R input, size_t index);
Cycle!(R) cycle(R)(ref R input, size_t index = 0);
指定された前進レンジを無限に繰り返します。 元々のレンジが無限だった場合(Cycle を適用しても結果は変わらないので)、 Cycle はそのことを検出し、元のレンジ型へのaliasとして定義されます。 元のレンジがランダムアクセスならば、Cycle もランダムアクセスになり、初期インデックス index を受け取るコンストラクタを提供します。Cycle は、パフォーマンス上の理由で、静的配列に対しては特殊化した定義が適用されます。

Example:
assert(equal(take(cycle([1, 2][]), 5), [ 1, 2, 1, 2, 1 ][]));

Tip:
このテンプレートは、単純な環状バッファを実装する優れた方法です。

enum Topology;
コンテナを走査するレンジ (例: std.range.SListRange) が、 そのトポロジを変更できるかの制御ポリシーです。コンテナのトポロジとは、 要素の実際の内容を含まない、コンテナのスロットとリンク構造のことと理解してください。 例えば、3要素の単方向リンクリストは、 3つのセルを順に繋いだ形のトポロジを持ちます。 トポロジは、セルに格納されている内容とは無関係に、 3つのセルが連結された形によってのみ決まります。

fixed
レンジからコンテナのトポロジを変更することはできない (一方、内容の変更は可能)。これは、 例えばコンテナ側でスロットの作成や破棄を制御しなければならない場合に有用です。

flexible
レンジからコンテナのトポロジを変更できる。 これは、 レンジが特定のコンテナに所有される類のものでないときに有用です。

struct SListRange(T,Topology topology = Topology.flexible);
シンプルで効率的な単方向リンクリストの定義です。 このリストは前進レンジとなっています。デフォルトのトポロジは flexible で、 要素の追加が可能となっています。

Example:
SListRange!(int, Topology.flexible) lst(2, 3);
lst = cons(1, lst);
assert(equal(lst, [1, 2, 3][]));

this(T[] values...);
値の配列をとるコンストラクタ

Example:
auto lst = SListRange!(int)(1, 2, 3, 4, 5);
assert(equal(lst, [1, 2, 3, 4, 5][]));

const bool empty();
返す要素がもうないときに true を返す、レンジの基本操作です。

void popFront();
レンジを次の要素に進める基本操作です。

T front();
現在の先頭要素を返すレンジの基本操作です。

const bool sameHead(in SListRange!(T,topology) rhs);
thisrhs の先頭が同じときに true を返します。

SListRange!(T,t) cons(T, Topology t)(T front, SListRange!(T,t) tail);
value を先頭に追加したリストを返します。

struct Zip(R...) if (R.length && allSatisfy!(isInputRange,R));
Zip!(R) zip(R...)(R ranges);
Zip!(R) zip(R...)(StoppingPolicy sp, R ranges);
複数のレンジを並行してたどります。 Zipレンジの要素型はプロキシとなっているタプルで、n番目のレンジに対応する値は e.at!(n) で取得できます。

Example:
int[] a = [ 1, 2, 3 ];
string[] b = [ "a", "b", "c" ];
// prints 1:a 2:b 3:c
foreach (e; zip(a, b))
{
    write(e.at!(0), ':', e.at!(1), ' ');
}
Zip は、渡されたレンジの最大公約数的な機能を提供します。 たとえば、全てのレンジがランダムアクセス可能ならランダムアクセス可能になりますし、 書き換えやswapが可能ならZipレンジでもそれは可能です。この機能によって、Zip は複数レンジを同時に操作できる非常に強力な機能となります。 たとえば、 以下のコードは2つの配列を並列してソートします。

int[] a = [ 1, 2, 3 ];
string[] b = [ "a", "b", "c" ];
sort!("a.at!(0) > b.at!(0)")(zip(a, b));
assert(a == [ 3, 2, 1 ]);
assert(b == [ "c", "b", "a" ]);

this(R rs, StoppingPolicy s = StoppingPolicy.shortest);
オブジェクトを構築します。通常、 std.range.zip 補助関数から間接的に呼び出されます。

bool empty();
レンジが終端に来ていれば true を返します。 終端の定義はStoppingPolicy依存です。

Proxy front();
現在の先頭要素へのプロキシを返します。

Proxy back();
現在の末尾要素へのプロキシを返します。

void popFront();
全てのレンジを popFront で進めます。

void popBack();
全てのレンジを popBack で縮めます。

size_t length();
このレンジの length を返します。全ての内蔵レンジが length を定義しているときのみ定義されます。

Zip!(R) opSlice(size_t from, size_t to);
スライスを返します。 全ての内蔵レンジがスライスを計算できる場合にのみ定義されます。

struct Proxy;
アクセス関数の返すプロキシ型です。

std.range.ElementType!(R[i]) at(int i)()
i 番目のレンジから現在の要素を取得します。

bool hasAt(int i)()
現在の要素が第i番目のレンジに存在するかどうかを返します。 この関数が false を返す可能性があるのは、 StoppingPolicy.longest ポリシーを選んだときのみです。

Proxy opIndex(size_t n);
全レンジの第n要素を束ねたレンジを返します。 全てのレンジがランダムアクセスなときにのみ定義されます。

enum StoppingPolicy;
Zip の終了条件を定義します。 デフォルトでは、一番短いレンジの長さに合わせます。

shortest
一番短いレンジが終わったタイミングで終了

longest
一番長いレンジが終わったタイミングで終了

requireSameLength
全てのレンジが同じ長さであることを要求

struct Recurrence(alias fun,StateType,size_t stateSize);
Recurrence!(fun,CommonType!(State),State.length) recurrence(alias fun, State...)(State initial);
初期値と、前の値から次の値を計算する漸化式を与えて、 数列を生成します。 結果は無限の前進レンジとなります。 Recurrence 型を直接宣言することは稀で、 多くの場合は、補助関数 recurrence を使ってレンジを作成します。

recurrence を呼び出すと、テンプレート引数で指定された式を使って、 通常の引数で指定された初期値から次の値を計算します。 例えば、 フィボナッチ数列の場合、 直前の2つの値を使って次のフィボナッチ数を計算する必要があるため、 初期値は2つとなります(従ってサイズ2のステートを持ちます)。

文字列形式で関数を渡す場合、状態は "a" という名前を持ち、数列の何番目の要素であるかを表す "n" という名前のゼロベースのインデックスを持ちます。 文字列では、a[n] の値を a[n - 1], a[n - 2], a[n - 3],..., a[n - stateSize] から計算する式を書きます。 ステート数は、 recurrence に渡された実引数の個数で決まります。 ステート数やその管理は適切にRecurrenceの内部で行われます。

Example:
// a[0] = 1, a[1] = 1, 以下 a[n+1] = a[n-1] + a[n] で計算
auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1);
// 最初の10個のフィボナッチ数を表示
foreach (e; take(fib, 10)) { writeln(e); }
// 最初の10個の階乗数を表示
foreach (e; take(recurrence!("a[n-1] * n")(1), 10)) { writeln(e); }

struct Sequence(alias fun,State);
Sequence!(fun,Tuple!(State)) sequence(alias fun, State...)(State args);
SequenceRecurrence eと似ていますが、 数式が closed form であることが違う点です。これはつまり、数列のn番目の値は 初期値と n そのものから直接計算できるという意味です。 結果として、Sequence はランダムアクセスレンジとなり、通常の Recurrence が前進レンジにしかならないのと対照的です。

ステートは Tuple で保持されるため、 違う型の要素を持たせることも可能です。

Example:
// a[0] = 1, a[1] = 2, a[n] = a[0] + n * a[1]
auto odds = sequence!("a[0] + n * a[1]")(1, 2);

Take!(Sequence!("a.field[0] + n * a.field[1]",Tuple!(CommonType!(B,E),S))) iota(B, E, S)(B begin, E end, S step);
Take!(Sequence!("a.field[0] + n * a.field[1]",Tuple!(CommonType!(B,E),uint))) iota(B, E)(B begin, E end);
begin, begin + step, begin + 2 * step, ..., と進めて end まで(ただしendは含まない)の部分列をレンジとして取り出します。結果はランダムアクセスレンジになります。 2引数バージョンは step = 1 の扱いになります。

Example:
auto r = iota(0, 10, 1);
assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][]));
r = iota(0, 11, 3);
assert(equal(r, [0, 3, 6, 9][]));
assert(r[2] == 6);
auto rf = iota(0.0, 0.5, 0.1);
assert(equal(rf, [0.0, 0.1, 0.2, 0.3, 0.4]));

enum TransverseOptions;
FrontTransversalTransversal (下参照) に対するオプション

assumeJagged
レンジ内の各レンジは異なる長さであると仮定されます。 (例: jagged array)

enforceNotJagged
レンジ内の全てのレンジは同じ長さであることが強制されます。 (例: 長さが一定の二次元配列) チェックは構築時に最初に一度だけ行われます。

assumeNotJagged
チェックなしで、レンジ内の全てのレンジは同じ長さであることを仮定します。 このオプションは、 既に長さが一定であることをチェック済みのレンジに適用するときなどに有用です。

struct FrontTransversal(RangeOfRanges,TransverseOptions opt = TransverseOptions.assumeJagged);
FrontTransversal!(RangeOfRanges,opt) frontTransversal(TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges)(RangeOfRanges rr);
レンジのレンジを受け取り、 それぞれの先頭要素を順に列挙します。

Example:
int[][] x = new int[][2];
x[0] = [1, 2];
x[1] = [3, 4];
auto ror = frontTransversal(x);
assert(equals(ror, [ 1, 3 ][]));

this(RangeOfRanges input);
入力からの構築

bool empty();
ElementType front();
void popFront();
前進レンジの基本操作

ElementType back();
void popBack();
双方向レンジの基本操作。isBidirectionalRange!RangeOfRanges が真なときに提供されます。

ElementType opIndex(size_t n);
ランダムアクセスレンジの基本操作。 isRandomAccessRange!RangeOfRanges && (opt == TransverseOptions.assumeNotJagged || opt == TransverseOptions.enforceNotJagged) が真なときに提供されます。

struct Transversal(RangeOfRanges,TransverseOptions opt = TransverseOptions.assumeJagged);
Transversal!(RangeOfRanges,opt) transversal(TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges)(RangeOfRanges rr, size_t n);
レンジのレンジを受け取り、それぞれの第 n 要素を順に列挙します。 ランダムアクセスレンジのレンジを渡す必要があります。

Example:
int[][] x = new int[][2];
x[0] = [1, 2];
x[1] = [3, 4];
auto ror = transversal(x, 1);
assert(equals(ror, [ 2, 4 ][]));

this(RangeOfRanges input, size_t n);
入力とインデックスから構築

bool empty();
ElementType front();
void popFront();
前進レンジの基本操作

ElementType back();
双方向レンジの基本操作。isBidirectionalRange!RangeOfRanges が真なときに提供されます。

ElementType opIndex(size_t n);
ランダムアクセスレンジの基本操作。isRandomAccessRange!RangeOfRanges && (opt == TransverseOptions.assumeNotJagged || opt == TransverseOptions.enforceNotJagged) が真なときに提供されます。