std.range
This module defines the notion of range (by the membership tests isInputRange, isForwardRange, isBidirectionalRange, isRandomAccessRange), range capability tests (such as hasLength or hasSlicing), and a few useful range incarnations. Source:std/range.d License:
Boost License 1.0. Authors:
Andrei Alexandrescu, David Simcha. Credit for some of the ideas in building this module goes to Leonardo Maffi.
- Returns true if R is an input range. An input range must
define the primitives empty, popFront, and front. The
following code should compile for any input range.
R r; // can define a range object if (r.empty) {} // can test for empty r.popFront(); // can invoke popFront() auto h = r.front; // can get the front of the range of non-void type
The semantics of an input range (not checkable during compilation) are assumed to be the following (r is an object of type R):- r.empty returns false iff there is more data available in the range.
- r.front returns the current element in the range. It may return by value or by reference. Calling r.front is allowed only if calling r.empty has, or would have, returned false.
- r.popFront advances to the next element in the range. Calling r.popFront is allowed only if calling r.empty has, or would have, returned false.
- Outputs e to r. The exact effect is dependent upon the two types. which must be an output range. Several cases are accepted, as described below. The code snippets are attempted in order, and the first to compile "wins" and gets evaluated.
- Returns true if R is an output range for elements of type E. An output range is defined functionally as a range that supports the operation put(r, e) as defined above.
- Returns true if R is a forward range. A forward range is an
input range r that can save "checkpoints" by saving r.save
to another value of type R. Notable examples of input ranges that
are not forward ranges are file/socket ranges; copying such a
range will not save the position in the stream, and they most likely
reuse an internal buffer as the entire stream does not sit in
memory. Subsequently, advancing either the original or the copy will
advance the stream, so the copies are not independent.
The following code should compile for any forward range.
static assert(isInputRange!R); R r1; R r2 = r1.save; // can save the current position into another range
Saving a range is not duplicating it; in the example above, r1 and r2 still refer to the same underlying data. They just navigate that data independently. The semantics of a forward range (not checkable during compilation) are the same as for an input range, with the additional requirement that backtracking must be possible by saving a copy of the range object with save and using it later. - Returns true if R is a bidirectional range. A bidirectional
range is a forward range that also offers the primitives back and
popBack. The following code should compile for any bidirectional
range.
R r; static assert(isForwardRange!R); // is forward range r.popBack(); // can invoke popBack auto t = r.back; // can get the back of the range auto w = r.front; static assert(is(typeof(t) == typeof(w))); // same type for front and back
The semantics of a bidirectional range (not checkable during compilation) are assumed to be the following (r is an object of type R):- r.back returns (possibly a reference to) the last element in the range. Calling r.back is allowed only if calling r.empty has, or would have, returned false.
- Returns true if R is a random-access range. A random-access
range is a bidirectional range that also offers the primitive opIndex, OR an infinite forward range that offers opIndex. In
either case, the range must either offer length or be
infinite. The following code should compile for any random-access
range.
R r; static assert(isForwardRange!R); // range is forward static assert(isBidirectionalRange!R || isInfinite!R); // range is bidirectional or infinite auto e = r[1]; // can index
The semantics of a random-access range (not checkable during compilation) are assumed to be the following (r is an object of type R):- r.opIndex(n) returns a reference to the nth element in the range.
- Returns true iff the range supports the moveFront primitive, as well as moveBack and moveAt if it's a bidirectional or random access range. These may be explicitly implemented, or may work via the default behavior of the module level functions moveFront and friends.
- The element type of R. R does not have to be a range. The element type is determined as the type yielded by r.front for an object r or type R. For example, ElementType!(T[]) is T. If R is not a range, ElementType!R is void.
- The encoding element type of R. For narrow strings (char[], wchar[] and their qualified variants including string and wstring), ElementEncodingType is the character type of the string. For all other ranges, ElementEncodingType is the same as ElementType.
- Returns true if R is a forward range and has swappable
elements. The following code should compile for any random-access
range.
R r; static assert(isForwardRange!(R)); // range is forward swap(r.front, r.front); // can swap elements of the range
- Returns true if R is a forward range and has mutable
elements. The following code should compile for any random-access
range.
R r; static assert(isForwardRange!R); // range is forward auto e = r.front; r.front = e; // can assign elements of the range
- Tests whether R has lvalue elements. These are defined as elements that can be passed by reference and have their address taken.
- Returns true if R has a length member that returns an integral type. R does not have to be a range. Note that length is an optional primitive as no range must implement it. Some ranges do not store their length explicitly, some cannot compute it without actually exhausting the range (e.g. socket streams), and some other ranges may be infinite. Although narrow string types (char[], wchar[], and their qualified derivatives) do define a length property, hasLength yields false for them. This is because a narrow string's length does not reflect the number of characters, but instead the number of encoding units, and as such is not useful with range-oriented algorithms.
- Returns true if Range is an infinite input range. An
infinite input range is an input range that has a statically-defined
enumerated member called empty that is always false, for
example:
struct MyInfiniteRange { enum bool empty = false; ... }
- Returns true if Range offers a slicing operator with
integral boundaries, that in turn returns an input range type. The
following code should compile for hasSlicing to be true:
Range r; auto s = r[1 .. 2]; static assert(isInputRange!(typeof(s)));
- This is a best-effort implementation of length for any kind of range. If hasLength!(Range), simply returns range.length without checking upTo. Otherwise, walks the range through its length and returns the number of elements seen. Performes Ο(n) evaluations of range.empty and range.popFront(), where n is the effective length of range. The upTo parameter is useful to "cut the losses" in case the interest is in seeing whether the range has at least some number of elements. If the parameter upTo is specified, stops if upTo steps have been taken and returns upTo.
- Iterates a bidirectional range backwards. The original range can be
accessed by using the source property. Applying retro twice to
the same range yields the original range.
Example:
int[] a = [ 1, 2, 3, 4, 5 ]; assert(equal(retro(a), [ 5, 4, 3, 2, 1 ][])); assert(retro(a).source is a); assert(retro(retro(a)) is a);
- Iterates range r with stride n. If the range is a
random-access range, moves by indexing into the range; otehrwise,
moves by successive calls to popFront. Applying stride twice to
the same range results in a stride that with a step that is the
product of the two applications.
Throws:
Exception if n == 0. Example:
int[] a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]; assert(equal(stride(a, 3), [ 1, 4, 7, 10 ][])); assert(stride(stride(a, 2), 3) == stride(a, 6));
- Spans multiple ranges in sequence. The function chain takes any
number of ranges and returns a Chain!(R1, R2,...) object. The
ranges may be different, but they must have the same element type. The
result is a range that offers the front, popFront, and empty primitives. If all input ranges offer random access and length, Chain offers them as well.
If only one range is offered to Chain or chain, the Chain type exits the picture by aliasing itself directly to that
range's type.
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][]));
- roundRobin(r1, r2, r3) yields r1.front, then r2.front,
then r3.front, after which it pops off one element from each and
continues again from r1. For example, if two ranges are involved,
it alternately yields elements off the two ranges. roundRobin
stops after it has consumed all ranges (skipping over the ones that
finish early).
Example:
int[] a = [ 1, 2, 3, 4]; int[] b = [ 10, 20 ]; assert(equal(roundRobin(a, b), [1, 10, 2, 20, 3, 4]));
- Iterates a random-access range starting from a given point and
progressively extending left and right from that point. If no initial
point is given, iteration starts from the middle of the
range. Iteration spans the entire range.
Example:
int[] a = [ 1, 2, 3, 4, 5 ]; assert(equal(radial(a), [ 3, 4, 2, 5, 1 ])); a = [ 1, 2, 3, 4 ]; assert(equal(radial(a), [ 2, 3, 1, 4 ]));
- Lazily takes only up to n elements of a range. This is
particularly useful when using with infinite ranges. If the range
offers random access and length, Take offers them as well.
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 ][]));
- Similar to , but assumes that range has at least n elements. Consequently, the result of takeExactly(range, n) always defines the length property (and initializes it to n) even when range itself does not define length. If R has slicing, takeExactly simply returns a slice of range. Otherwise if R is an input range, the type of the result is an input range with length. Finally, if R is a forward range (including bidirectional), the type of the result is a forward range with length.
- Returns a range with at most one element; for example, takeOne([42, 43, 44]) returns a range consisting of the integer 42. Calling popFront() off that range renders it empty.
Sometimes an empty range with the same signature is needed. For such
ranges use takeNone!R(). For example:
auto s = takeOne([42, 43, 44]); static assert(isRandomAccessRange!(typeof(s))); assert(s.length == 1); assert(!s.empty); assert(s.front == 42); s.front() = 43; assert(s.front == 43); assert(s.back == 43); assert(s[0] == 43); s.popFront(); assert(s.length == 0); assert(s.empty); s = takeNone!(int[])(); assert(s.length == 0); assert(s.empty);
In effect takeOne(r) is somewhat equivalent to and takeNone(r) is equivalent to take(r, 0), but in certain interfaces it is important to know statically that the range may only have at most one element. The type returned by takeOne and takeNone is a random-access range with length regardless of R's capability (another feature that distinguishes takeOne/takeNone from take). - Convenience function which calls (range, n) and
returns range.
Examples:
assert(drop([0, 2, 1, 5, 0, 3], 3) == [5, 0, 3]); assert(drop("hello world", 6) == "world"); assert(drop("hello world", 50).empty); assert(equal(drop(take("hello world", 6), 3), "lo "));
- Eagerly advances r itself (not a copy) n times (by calling
r.popFront at most n times). The pass of r into popFrontN is by reference, so the original range is
affected. Completes in Ο(1) steps for ranges that support
slicing, and in Ο(n) time for all other ranges.
Example:
int[] a = [ 1, 2, 3, 4, 5 ]; a.popFrontN(2); assert(a == [ 3, 4, 5 ]);
- Eagerly reduces r itself (not a copy) n times from its right
side (by calling r.popBack n times). The pass of r into
popBackN is by reference, so the original range is
affected. Completes in Ο(1) steps for ranges that support
slicing, and in Ο(n) time for all other ranges.
Returns the actual number of elements popped.
Example:
int[] a = [ 1, 2, 3, 4, 5 ]; a.popBackN(2); assert(a == [ 1, 2, 3 ]);
- Repeats one value forever.
Example:
enforce(equal(take(repeat(5), 4), [ 5, 5, 5, 5 ][]));
- Range primitive implementations.
- Repeats value exactly n times. Equivalent to take(repeat(value), n).
- Equivalent to repeat(value, n). Scheduled for deprecation.
- struct Cycle(Range) if (isForwardRange!(Unqual!(Range)) && !isInfinite!(Unqual!(Range)));
template Cycle(R) if (isInfinite!(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)(R input);
Cycle!(R) cycle(R)(ref R input, size_t index = 0); - Repeats the given forward range ad infinitum. If the original range is
infinite (fact that would make Cycle the identity application),
Cycle detects that and aliases itself to the range type
itself. If the original range has random access, Cycle offers
random access and also offers a constructor taking an initial position
index. Cycle is specialized for statically-sized arrays,
mostly for performance reasons.
Example:
assert(equal(take(cycle([1, 2][]), 5), [ 1, 2, 1, 2, 1 ][]));
Tip:
This is a great way to implement simple circular buffers.- Range primitive implementations.
- Iterate several ranges in lockstep. The element type is a proxy tuple
that allows accessing the current element in the nth range by
using e[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[0], ':', e[1], ' '); }
Zip offers the lowest range facilities of all components, e.g. it offers random access iff all ranges offer random access, and also offers mutation and swapping if all ranges offer it. Due to this, Zip is extremely powerful because it allows manipulating several ranges in lockstep. For example, the following code sorts two arrays in parallel:int[] a = [ 1, 2, 3 ]; string[] b = [ "a", "b", "c" ]; sort!("a[0] > b[0]")(zip(a, b)); assert(a == [ 3, 2, 1 ]); assert(b == [ "c", "b", "a" ]);
- this(R rs, StoppingPolicy s = StoppingPolicy.shortest);
- Builds an object. Usually this is invoked indirectly by using the function.
- Returns true if the range is at end. The test depends on the stopping policy.
- Returns the current iterated element.
- Sets the front of all iterated ranges.
- Moves out the front.
- Returns the rightmost element.
- Moves out the back. Returns the rightmost element.
- Returns the current iterated element. Returns the rightmost element.
- Advances to the next element in all controlled ranges.
- Calls popBack for all controlled ranges.
- Returns the length of this range. Defined only if all ranges define length.
- Returns a slice of the range. Defined only if all range define slicing.
- Returns the nth element in the composite range. Defined if all ranges offer random access.
- Assigns to the nth element in the composite range. Defined if all ranges offer random access.
- Destructively reads the nth element in the composite range. Defined if all ranges offer random access.
- Dictates how iteration in a Zip should stop. By default stop at the end of the shortest of all ranges.
- Iterate multiple ranges in lockstep using a foreach loop. If only a single
range is passed in, the Lockstep aliases itself away. If the
ranges are of different lengths and s == StoppingPolicy.shortest
stop after the shortest range is empty. If the ranges are of different
lengths and s == StoppingPolicy.requireSameLength, throw an
exception. s may not be StoppingPolicy.longest, and passing this
will throw an exception.
BUGS:
If a range does not offer lvalue access, but ref is used in the foreach loop, it will be silently accepted but any modifications to the variable will not be propagated to the underlying range. Examples:auto arr1 = [1,2,3,4,5]; auto arr2 = [6,7,8,9,10]; foreach(ref a, ref b; lockstep(arr1, arr2)) { a += b; } assert(arr1 == [7,9,11,13,15]); // Lockstep also supports iterating with an index variable: foreach(index, a, b; lockstep(arr1, arr2)) { writefln("Index %s: a = %s, b = %s", index, a, b); }
- Creates a mathematical sequence given the initial values and a
recurrence function that computes the next value from the existing
values. The sequence comes in the form of an infinite forward
range. The type Recurrence itself is seldom used directly; most
often, recurrences are obtained by calling the function recurrence.
When calling recurrence, the function that computes the next
value is specified as a template argument, and the initial values in
the recurrence are passed as regular arguments. For example, in a
Fibonacci sequence, there are two initial values (and therefore a
state size of 2) because computing the next Fibonacci value needs the
past two values.
If the function is passed in string form, the state has name "a"
and the zero-based index in the recurrence has name "n". The
given string must return the desired value for a[n] given a[n
- 1], a[n - 2], a[n - 3],..., a[n - stateSize]. The
state size is dictated by the number of arguments passed to the call
to recurrence. The Recurrence class itself takes care of
managing the recurrence's state and shifting it appropriately.
Example:
// a[0] = 1, a[1] = 1, and compute a[n+1] = a[n-1] + a[n] auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1); // print the first 10 Fibonacci numbers foreach (e; take(fib, 10)) { writeln(e); } // print the first 10 factorials foreach (e; take(recurrence!("a[n-1] * n")(1), 10)) { writeln(e); }
- Sequence is similar to Recurrence except that iteration is
presented in the so-called closed form. This means that the nth element in the series is
computable directly from the initial values and n itself. This
implies that the interface offered by Sequence is a random-access
range, as opposed to the regular Recurrence, which only offers
forward iteration.
The state of the sequence is stored as a Tuple so it can be
heterogeneous.
Example:
// a[0] = 1, a[1] = 2, a[n] = a[0] + n * a[1] auto odds = sequence!("a[0] + n * a[1]")(1, 2);
- Returns a range that goes through the numbers begin, begin +
step, begin + 2 * step, ..., up to and excluding end. The range offered is a random access range. The two-arguments
version has step = 1. If begin < end && step <= 0 or begin > end && step >= 0, then an empty range is returned. If begin != end and step == 0, an exception is thrown.
Throws:
Exception if step == 0 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(approxEqual(rf, [0.0, 0.1, 0.2, 0.3, 0.4]));
- Options for the FrontTransversal and Transversal ranges
(below).
- When transversed, the elements of a range of ranges are assumed to have different lengths (e.g. a jagged array).
- The transversal enforces that the elements of a range of ranges have all the same length (e.g. an array of arrays, all having the same length). Checking is done once upon construction of the transversal range.
- The transversal assumes, without verifying, that the elements of a range of ranges have all the same length. This option is useful if checking was already done from the outside of the range.
- Given a range of ranges, iterate transversally through the first
elements of each of the enclosed ranges.
Example:
int[][] x = new int[][2]; x[0] = [1, 2]; x[1] = [3, 4]; auto ror = frontTransversal(x); assert(equal(ror, [ 1, 3 ][]));
- this(RangeOfRanges input);
- Construction from an input.
- Forward range primitives.
- Bidirectional primitives. They are offered if isBidirectionalRange!RangeOfRanges.
- Random-access primitive. It is offered if isRandomAccessRange!RangeOfRanges && (opt == TransverseOptions.assumeNotJagged || opt == TransverseOptions.enforceNotJagged).
- Slicing if offered if RangeOfRanges supports slicing and all the conditions for supporting indexing are met.
- Given a range of ranges, iterate transversally through the the nth element of each of the enclosed ranges. All elements of the
enclosing range must offer random access.
Example:
int[][] x = new int[][2]; x[0] = [1, 2]; x[1] = [3, 4]; auto ror = transversal(x, 1); assert(equal(ror, [ 2, 4 ][]));
- this(RangeOfRanges input, size_t n);
- Construction from an input and an index.
- Forward range primitives.
- Bidirectional primitives. They are offered if isBidirectionalRange!RangeOfRanges.
- Random-access primitive. It is offered if isRandomAccessRange!RangeOfRanges && (opt == TransverseOptions.assumeNotJagged || opt == TransverseOptions.enforceNotJagged).
- Slicing if offered if RangeOfRanges supports slicing and all the conditions for supporting indexing are met.
- This struct takes two ranges, source and indices, and creates a view
of source as if its elements were reordered according to indices.
indices may include only a subset of the elements of source and
may also repeat elements.
Source must be a random access range. The returned range will be
bidirectional or random-access if Indices is bidirectional or
random-access, respectively.
Examples:
auto source = [1, 2, 3, 4, 5]; auto indices = [4, 3, 1, 2, 0, 4]; auto ind = indexed(source, indices); assert(equal(ind, [5, 4, 2, 3, 1, 5])); // When elements of indices are duplicated and Source has lvalue elements, // these are aliased in ind. ind[0]++; assert(ind[0] == 6); assert(ind[5] == 6);
- @property ref auto front();
void popFront();
typeof(this) save();
@property ref auto front(ElementType!(Source) newVal);
auto moveFront();
@property ref auto back();
void popBack();
@property ref auto back(ElementType!(Source) newVal);
auto moveBack();
size_t length();
ref auto opIndex(size_t index);
typeof(this) opSlice(size_t a, size_t b);
auto opIndexAssign(ElementType!(Source) newVal, size_t index);
auto moveAt(size_t index); - Range primitives
- Returns the source range.
- Returns the indices range.
- Returns the physical index into the source range corresponding to a
given logical index. This is useful, for example, when indexing
an Indexed without adding another layer of indirection.
Examples:
auto ind = indexed([1, 2, 3, 4, 5], [1, 3, 4]); assert(ind.physicalIndex(0) == 1);
- This range iterates over fixed-sized chunks of size chunkSize of a
source range. Source must be an input range with slicing and length.
If source.length is not evenly divisible by chunkSize, the back
element of this range will contain fewer than chunkSize elements.
Examples:
auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; auto chunks = chunks(source, 4); assert(chunks[0] == [1, 2, 3, 4]); assert(chunks[1] == [5, 6, 7, 8]); assert(chunks[2] == [9, 10]); assert(chunks.back == chunks[2]); assert(chunks.front == chunks[0]); assert(chunks.length == 3);
- this(Source source, size_t chunkSize);
- Range primitives.
- Moves the front of r out and returns it. Leaves r.front in a destroyable state that does not allocate any resources (usually equal to its .init value).
- Moves the back of r out and returns it. Leaves r.back in a destroyable state that does not allocate any resources (usually equal to its .init value).
- Moves element at index i of r out and returns it. Leaves r.front in a destroyable state that does not allocate any resources (usually equal to its .init value).
- These interfaces are intended to provide virtual function-based wrappers
around input ranges with element type E. This is useful where a well-defined
binary interface is required, such as when a DLL function or virtual function
needs to accept a generic range as a parameter. Note that
isInputRange and friends check for conformance to structural
interfaces, not for implementation of these interface types.
Examples:
class UsesRanges { void useRange(InputRange range) { // Function body. } } // Create a range type. auto squares = map!"a * a"(iota(10)); // Wrap it in an interface. auto squaresWrapped = inputRangeObject(squares); // Use it. auto usesRanges = new UsesRanges; usesRanges.useRange(squaresWrapped);
Limitations:
These interfaces are not capable of forwarding ref access to elements. Infiniteness of the wrapped range is not propagated. Length is not propagated in the case of non-random access ranges.- foreach iteration uses opApply, since one delegate call per loop
iteration is faster than three virtual function calls.
BUGS:
If a ref variable is provided as the loop variable, changes made to the loop variable will not be propagated to the underlying range. If the address of the loop variable is escaped, undefined behavior will result. This is related to DMD bug 2443.
- Interface for a forward range of type E.
- Interface for a bidirectional range of type E.
- Interface for a finite random access range of type E.
- Interface for an infinite random access range of type E.
- Adds assignable elements to InputRange.
- Adds assignable elements to ForwardRange.
- Adds assignable elements to BidirectionalRange.
- Adds assignable elements to RandomAccessFinite.
- Interface for an output range of type E. Usage is similar to the
InputRange interface and descendants.
- Implements the OutputRange interface for all types E and wraps the put method for each type E in a virtual function.
- Returns the interface type that best matches R.
- Implements the most derived interface that R works with and wraps all relevant range primitives in virtual functions. If R is already derived from the InputRange interface, aliases itself away.
- Convenience function for creating a InputRangeObject of the proper type.
- Convenience function for creating a OutputRangeObject with a base range
of type R that accepts types E.
Examples:
uint[] outputArray; auto app = appender(&outputArray); auto appWrapped = outputRangeObject!(uint, uint[])(app); static assert(is(typeof(appWrapped) : OutputRange!(uint[]))); static assert(is(typeof(appWrapped) : OutputRange!(uint)));
- Policy used with the searching primitives lowerBound, upperBound, and equalRange of below.
- Searches with a step that is grows linearly (1, 2, 3,...) leading to a quadratic search schedule (indexes tried are 0, 1, 3, 5, 8, 12, 17, 23,...) Once the search overshoots its target, the remaining interval is searched using binary search. The search is completed in Ο(sqrt(n)) time. Use it when you are reasonably confident that the value is around the beginning of the range.
- Performs a galloping search algorithm, i.e. searches with a step that doubles every time, (1, 2, 4, 8, ...) leading to an exponential search schedule (indexes tried are 0, 1, 2, 4, 8, 16, 32,...) Once the search overshoots its target, the remaining interval is searched using binary search. A value is found in Ο(log(n)) time.
- Searches using a classic interval halving policy. The search starts in the middle of the range, and each search step cuts the range in half. This policy finds a value in Ο(log(n)) time but is less cache friendly than gallop for large ranges. The binarySearch policy is used as the last step of trot, gallop, trotBackwards, and gallopBackwards strategies.
- Similar to trot but starts backwards. Use it when confident that the value is around the end of the range.
- Similar to gallop but starts backwards. Use it when confident that the value is around the end of the range.
- Represents a sorted random-access range. In addition to the regular
range primitives, supports fast operations using binary search. To
obtain a SortedRange from an unsorted range r, use
std.algorithm.sort which sorts r in place and returns the
corresponding SortedRange. To construct a SortedRange
from a range r that is known to be already sorted, use
described below.
Example:
auto a = [ 1, 2, 3, 42, 52, 64 ]; auto r = assumeSorted(a); assert(r.canFind(3)); assert(!r.canFind(32)); auto r1 = sort!"a > b"(a); assert(r1.canFind(3)); assert(!r1.canFind(32)); assert(r1.release() == [ 64, 52, 42, 3, 2, 1 ]);
SortedRange could accept ranges weaker than random-access, but it is unable to provide interesting functionality for them. Therefore, SortedRange is currently restricted to random-access ranges. No copy of the original range is ever made. If the underlying range is changed concurrently with its corresponding SortedRange in ways that break its sortedness, SortedRange will work erratically. Example:
auto a = [ 1, 2, 3, 42, 52, 64 ]; auto r = assumeSorted(a); assert(r.canFind(42)); swap(a[3], a[5]); // illegal to break sortedness of original range assert(!r.canFind(42)); // passes although it shouldn't
- Range primitives.
- Releases the controlled range and returns it.
- This function uses binary search with policy sp to find the
largest left subrange on which pred(x, value) is true for
all x (e.g., if pred is "less than", returns the portion of
the range with elements strictly smaller than value). The search
schedule and its complexity are documented in
. See also STL's lower_bound.
Example:
auto a = assumeSorted([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]); auto p = a.lowerBound(4); assert(equal(p, [ 0, 1, 2, 3 ]));
- This function uses binary search with policy sp to find the
largest right subrange on which pred(value, x) is true
for all x (e.g., if pred is "less than", returns the
portion of the range with elements strictly greater than value). The search schedule and its complexity are documented in
. See also STL's upper_bound.
Example:
auto a = assumeSorted([ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]); auto p = a.upperBound(3); assert(equal(p, [4, 4, 5, 6]));
- Returns the subrange containing all elements e for which both pred(e, value) and pred(value, e) evaluate to false (e.g.,
if pred is "less than", returns the portion of the range with
elements equal to value). Uses a classic binary search with
interval halving until it finds a value that satisfies the condition,
then uses SearchPolicy.gallopBackwards to find the left boundary
and SearchPolicy.gallop to find the right boundary. These
policies are justified by the fact that the two boundaries are likely
to be near the first found value (i.e., equal ranges are relatively
small). Completes the entire search in Ο(log(n)) time. See also
STL's equal_range.
Example:
auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; auto r = equalRange(a, 3); assert(equal(r, [ 3, 3, 3 ]));
- Returns a tuple r such that r[0] is the same as the result
of lowerBound(value), r[1] is the same as the result of equalRange(value), and r[2] is the same as the result of upperBound(value). The call is faster than computing all three
separately. Uses a search schedule similar to equalRange. Completes the entire search in Ο(log(n)) time.
Example:
auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; auto r = assumeSorted(a).trisect(3); assert(equal(r[0], [ 1, 2 ])); assert(equal(r[1], [ 3, 3, 3 ])); assert(equal(r[2], [ 4, 4, 5, 6 ]));
- Returns true if and only if value can be found in range, which is assumed to be sorted. Performs Ο(log(r.length)) evaluations of pred. See also STL's binary_search.
- Deprecated alias for contains.
- Assumes r is sorted by predicate pred and returns the corresponding SortedRange!(pred, R) having r as support. To keep the checking costs low, the cost is Ο((1)) in release mode (no checks for sortedness are performed). In debug mode, a few random elements of r are checked for sortedness. The size of the sample is proportional Ο(log(r.length)). That way, checking has no effect on the complexity of subsequent operations specific to sorted ranges (such as binary search). The probability of an arbitrary unsorted range failing the test is very high (however, an almost-sorted range is likely to pass it). To check for sortedness at cost Ο(n), use std.algorithm.isSorted.