std.range
このモジュールは、レンジの有用な操作を定義しています。
このモジュールの構成のアイデアは
Leonardo Maffi によるものです。
License:Boost License 1.0.
Authors:Andrei Alexandrescu
- R が入力レンジ(Input Range)のとき true を返します。
入力レンジは基本操作 empty、popFront、front を提供する必要があります。
以下のコードはどんな入力レンジに対してもコンパイルが通ります。
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 を返します。
双方向レンジは、前進レンジの機能に加え基本操作 back と
popBack を提供する必要があります。
以下のコードはどんな双方向レンジに対してもコンパイルが通ります。
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番目の要素への参照を返す
- 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);
template
hasAssignableElements(R)
- R が前進レンジで代入可能な要素型を持つときに true
を返します。
以下のコードはどんなランダムアクセスレンジに対してもコンパイルが通ります。
R r;
static assert(isForwardRange!(R)); auto e = r.front;
r.front = e;
- R が、整数型の値を返す length メンバを持っているときに true を返します。
R は必ずしもレンジでなくても構いません。注意点として、length はオプション的な基本操作で、どのレンジもこれを実装することは必須ではありません。
レンジによっては明示的に長さを保持しない実装もありえますし、
実際にレンジの端まで行かなければ終端のわからないレンジ (ソケットストリーム等) もあり、
さらに無限の長さをもつレンジもあり得ます。
template
isInfinite(Range)
- 無限入力レンジに対して true を返します。
無限入力レンジは、
静的に定義された常に false となる empty メンバを持ちます。
例えば:
struct InfiniteRange
{
enum bool empty = false;
...
}
template
hasSlicing(Range)
- Range が整数を指定するスライス演算子を持ち、
入力レンジを返す時に、true となります。
以下のコードは hasSlicing が true の時にコンパイルが通ります:
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.empty
と range.popFront の呼び出し(ただし n は range の実際の長さ)を行います。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 ][]));
- input.empty へと転送されます
- input.popBack へと転送されます
- input.popFront へと転送されます
ElementType!(R)
opIndex(uint
n);
- input[input.length - n + 1] へと転送されます。R
がランダムアクセスレンジで R に R.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);
- ストライドを初期化
- this を返します。
- input.empty へと転送されます。
- @@@
- @@@
- input.front へと転送されます。
- 余分な要素を除いた後 input.back へと転送されます。
ElementType!(R)
opIndex(uint
n);
- input[input.length - n + 1] へと転送されます。R
がランダムアクセスレンジで R に R.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つだけ Chain や chain に渡された場合
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) を使用します。
- this を返します。
- 全ての要素を列挙し終わっていれば true を返す、
レンジの基本操作です
-
レンジを一つ次の要素に進める基本操作です
- 現在指している要素を返すレンジの基本操作です。
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 自身 (コピーではない) を、n 回
r.popFront を呼び出して先に進めます。r は popFrontN
に参照渡しされるので、元々のレンジが影響されます。スライス操作をサポートするレンジならば Ο(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 自身 (コピーではない) を、n 回
r.popBack を呼び出して終端を縮めます。
r は popBackN に参照渡しされるので、元々のレンジが影響されます。
スライス操作をサポートするレンジならば Ο(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:
このテンプレートは、単純な環状バッファを実装する優れた方法です。
コンテナを走査するレンジ (例: std.range.SListRange) が、
そのトポロジを変更できるかの制御ポリシーです。コンテナのトポロジとは、
要素の実際の内容を含まない、コンテナのスロットとリンク構造のことと理解してください。
例えば、3要素の単方向リンクリストは、
3つのセルを順に繋いだ形のトポロジを持ちます。
トポロジは、セルに格納されている内容とは無関係に、
3つのセルが連結された形によってのみ決まります。
- レンジからコンテナのトポロジを変更することはできない
(一方、内容の変更は可能)。これは、
例えばコンテナ側でスロットの作成や破棄を制御しなければならない場合に有用です。
- レンジからコンテナのトポロジを変更できる。
これは、
レンジが特定のコンテナに所有される類のものでないときに有用です。
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][]));
- 返す要素がもうないときに true
を返す、レンジの基本操作です。
- レンジを次の要素に進める基本操作です。
- 現在の先頭要素を返すレンジの基本操作です。
const bool
sameHead(in SListRange!(T,topology)
rhs);
- this と rhs の先頭が同じときに 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" ];
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 補助関数から間接的に呼び出されます。
- レンジが終端に来ていれば true を返します。
終端の定義はStoppingPolicy依存です。
- 現在の先頭要素へのプロキシを返します。
- 現在の末尾要素へのプロキシを返します。
- 全てのレンジを popFront で進めます。
- 全てのレンジを popBack で縮めます。
- このレンジの length を返します。全ての内蔵レンジが
length を定義しているときのみ定義されます。
Zip!(R)
opSlice(size_t
from, size_t
to);
- スライスを返します。
全ての内蔵レンジがスライスを計算できる場合にのみ定義されます。
- アクセス関数の返すプロキシ型です。
std.range.ElementType!(R[i])
at(int i)()
- 第 i 番目のレンジから現在の要素を取得します。
- 現在の要素が第i番目のレンジに存在するかどうかを返します。
この関数が false を返す可能性があるのは、
StoppingPolicy.longest ポリシーを選んだときのみです。
全レンジの第n要素を束ねたレンジを返します。
全てのレンジがランダムアクセスなときにのみ定義されます。
Zip の終了条件を定義します。
デフォルトでは、一番短いレンジの長さに合わせます。
- 一番短いレンジが終わったタイミングで終了
- 一番長いレンジが終わったタイミングで終了
- 全てのレンジが同じ長さであることを要求
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:
auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1);
foreach (e; take(fib, 10)) { writeln(e); }
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);
Sequence は Recurrence eと似ていますが、
数式が closed form であることが違う点です。これはつまり、数列のn番目の値は
初期値と n そのものから直接計算できるという意味です。
結果として、Sequence
はランダムアクセスレンジとなり、通常の Recurrence
が前進レンジにしかならないのと対照的です。
ステートは Tuple で保持されるため、
違う型の要素を持たせることも可能です。
Example:
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]));
FrontTransversal と Transversal
(下参照) に対するオプション
- レンジ内の各レンジは異なる長さであると仮定されます。
(例: jagged array)
- レンジ内の全てのレンジは同じ長さであることが強制されます。
(例:
長さが一定の二次元配列)
チェックは構築時に最初に一度だけ行われます。
- チェックなしで、レンジ内の全てのレンジは同じ長さであることを仮定します。
このオプションは、
既に長さが一定であることをチェック済みのレンジに適用するときなどに有用です。
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();
- 前進レンジの基本操作
- 双方向レンジの基本操作。isBidirectionalRange!RangeOfRanges が真なときに提供されます。
ElementType
opIndex(size_t
n);
- ランダムアクセスレンジの基本操作。isRandomAccessRange!RangeOfRanges && (opt ==
TransverseOptions.assumeNotJagged || opt ==
TransverseOptions.enforceNotJagged) が真なときに提供されます。
Copyright © 1999-2009 by Digital Mars, All Rights Reserved