Authors
Andrei Alexandrescu, David Simcha,
Jonathan M Davis, and Jack Stouffer. Credit
for some of the ideas in building this module goes to
Leonardo Maffi.This module defines the notion of a range. Ranges generalize the concept of arrays, lists, or anything that involves sequential access. This abstraction enables the same set of algorithms (see std.algorithm) to be used with a vast variety of different concrete types. For example, a linear search algorithm such as find works not just for arrays, but for linked-lists, input files, incoming network data, etc.
Guides:
There are many articles available that can bolster understanding ranges:
for the basics of working with and creating range-based code.
talk at DConf 2015 a vivid introduction from its core constructs to practical advice.
for an interactive introduction.
component programming with ranges
for a real-world showcase of the influence of range-based programming on complex algorithms.Submodules:
This module has two submodules:
The std.range.primitives submodule provides basic range functionality. It defines several templates for testing whether a given object is a range, what kind of range it is, and provides some common range operations.
The std.range.interfaces submodule provides object-based interfaces for working with ranges via runtime polymorphism.
The remainder of this module provides a rich set of range creation and composition templates that let you construct new ranges out of existing ranges:
Sortedness:
Ranges whose elements are sorted afford better efficiency with certain operations. For this, the assumeSorted function can be used to construct a SortedRange from a pre-sorted range. The sort function also conveniently returns a SortedRange. SortedRange objects provide some additional range operations that take advantage of the fact that the range is sorted.
Source: std/range/package.d
private size_t chosenIstatic auto ref actOnChosen(alias foo, ExtraArgs ...)(ref ChooseResult r, auto ref ExtraArgs extraArgs)void opAssign(ChooseResult r)@property auto ref front()void popFront()this(size_t chosen, return scope Ranges rs)front(T)Create a range which repeats one value.
value | the _value to repeat |
n | the number of times to repeat value |
n is not defined, an infinite random access range
with slicing.
If n is defined, a random access range with slicing.
DollarToken(functionAttributes!fun & FunctionAttribute.ref_) ? true : false returnByRef_false emptyRange primitivesRepeats 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. That works for non-forward ranges too. If the original range has random access, Cycle offers random access and also offers a constructor taking an initial position index. Cycle works with static arrays in addition to ranges, mostly for performance reasons.
Tip: This is a great way to implement simple circular buffers.
ditto
DollarTokenIterate 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].
zip is similar to lockstep, but lockstep doesn't bundle its elements and uses the opApply protocol. lockstep allows reference access to the elements in foreach iterations.
sp | controls what zip will do if the ranges are different lengths |
ranges | the ranges to zip together |
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.
Exception if all of the ranges are not the same length and
sp is set to StoppingPolicy.requireSameLength.
Limitations: The @nogc and nothrow attributes cannot be inferred for the Zip struct because StoppingPolicy can vary at runtime. This limitation is not shared by the anonymous range returned by the zip function when not given an explicit StoppingPolicy as an argument.
.ElementType!(R[i]) tryGetInit(size_t i)()void popFront()Advances to the next element in all controlled ranges.this(R rs, StoppingPolicy s = StoppingPolicy.shortest)Builds an object. Usually this is invoked indirectly by using the zip function.private Ranges rangesprivate bool isBackWellDefinedthis(Ranges rs)Iterate multiple ranges in lockstep using a foreach loop. In contrast to
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.
Iterating over Lockstep in reverse and with an index is only possible when s == StoppingPolicy.requireSameLength, in order to preserve indexes. If an attempt is made at iterating in reverse when s == StoppingPolicy.shortest, an exception will be thrown.
By default StoppingPolicy is set to StoppingPolicy.shortest.
private Ranges rangesprivate StoppingPolicy stoppingPolicyprivate LockstepMixin!Ranges(withIndex: false, reverse: false) lockstepMixinFFprivate LockstepMixin!Ranges(withIndex: true, reverse: false) lockstepMixinTFthis(Ranges ranges, StoppingPolicy sp = StoppingPolicy.shortest)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.
The signature of this function should be: ---- auto fun(R)(R state, size_t n) ---- where n will be the index of the current value, and state will be an opaque state vector that can be indexed with array-indexing notation state[i], where valid values of i range from (n - 1) to
(n - State.length).
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 struct itself takes care of managing the recurrence's state and shifting it appropriately.
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.
ElementType front() @propertyvoid popFront()auto opSlice(size_t lower, size_t upper)auto opSlice(size_t lower, DollarToken)ElementType opIndex(size_t n)DollarTokenOptions for the FrontTransversal and Transversal ranges (below).
Given a range of ranges, iterate transversally through the nth element of each of the enclosed ranges. This function is similar to unzip in other languages.
opt | Controls the assumptions the function makes about the lengths of the ranges |
rr | An input range of random access ranges |
and random access are given if the element type of rr provides them.
RangeOfRanges _inputsize_t _nthis(RangeOfRanges input, size_t n)Construction from an input and an index.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.
Source _sourceIndices _indicesthis(Source source, Indices indices)This range iterates over fixed-sized chunks of size chunkSize of a source range. Source must be an input range. chunkSize must be greater than zero.
If !isInfinite!Source and source.walkLength is not evenly divisible by chunkSize, the back element of this range will contain fewer than chunkSize elements.
If Source is a forward range, the resulting range will be forward ranges as well. Otherwise, the resulting chunks will be input ranges consuming the same input: iterating over front will shrink the chunk such that subsequent invocations of front will no longer return the full chunk, and calling popFront on the outer range will invalidate any lingering references to previous values of front.
source | Range from which the chunks will be selected |
chunkSize | Chunk size |
This range splits a source range into chunkCount chunks of approximately equal length. Source must be a forward range with known length.
Unlike chunks, evenChunks takes a chunk count (not size). The returned range will contain zero or more source.length / chunkCount + 1 elements followed by source.length / chunkCount elements. If source.length < chunkCount, some chunks will be empty.
chunkCount must not be zero, unless source is also empty.
Source _sourcesize_t _chunkCount@property auto front()Forward range primitives. Always present.void popFront()Dittosize_t _chunkPos(size_t i)this(Source source, size_t chunkCount)Standard constructorprivate Values.length arityprivate allSatisfy!(
ApplyRight!(isAssignable, CommonType!Values),
Values
) canAssignElementsprivate size_t frontIndexprivate size_t backIndexbool empty() @propertyCommonType!Values front() @propertyvoid popFront()CommonType!Values back() @propertyvoid popBack()OnlyResult save() @propertyCommonType!Values opIndex(size_t idx) @trustedOnlyResult opSlice(size_t from, size_t to)this(return scope ref Values values)Policy used with the searching primitives lowerBound, upperBound, and equalRange of SortedRange below.
Options for SortedRange ranges (below).
Represents a sorted range. In addition to the regular range primitives, supports additional operations that take advantage of the ordering, such as merge and binary search. To obtain a SortedRange from an unsorted range r, use
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 assumeSorted.
private Range _input@property auto ref front()Dittovoid popFront()Dittosize_t getTransitionIndex(SearchPolicy sp, alias test, V)(V v) if (sp == SearchPolicy.binarySearch && isRandomAccessRange!Range && hasLength!Range)size_t getTransitionIndex(SearchPolicy sp, alias test, V)(V v) if ((sp == SearchPolicy.trot || sp == SearchPolicy.gallop)
&& isRandomAccessRange!Range)size_t getTransitionIndex(SearchPolicy sp, alias test, V)(V v) if ((sp == SearchPolicy.trotBackwards || sp == SearchPolicy.gallopBackwards)
&& isRandomAccessRange!Range)auto lowerBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(V value) if (isTwoWayCompatible!(predFun, ElementType!Range, V)
&& hasSlicing!Range)This function uses a search with policy `sp` to find the largest left subrange on which pred(x) is `true` for all `x` (e.g., if `pred` is "less than", returns the portion of the range with elements...auto upperBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(V value) if (isTwoWayCompatible!(predFun, ElementType!Range, V))This function searches with policy `sp` to find the largest right subrange on which pred(value) is `true` for all `x` (e.g., if `pred` is "less than", returns the portion of the range with elements...auto equalRange(V)(V value) if (isTwoWayCompatible!(predFun, ElementType!Range, V)
&& isRandomAccessRange!Range)Returns the subrange containing all elements `e` for which both pred(e) and pred(value) evaluate to `false` (e.g., if `pred` is "less than", returns the portion of the range with elements equal to ...auto trisect(V)(V value) if (isTwoWayCompatible!(predFun, ElementType!Range, V)
&& isRandomAccessRange!Range && hasLength!Range)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(valu...bool contains(V)(V value) if (isRandomAccessRange!Range)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`.bool opBinaryRight(string op, V)(V value) if (op == "in" && isRandomAccessRange!Range)Like `contains`, but the value is specified before the range.auto groupBy()()Returns a range of subranges of elements that are equivalent according to the sorting relation.Wrapper which effectively makes it possible to pass a range by reference. Both the original range and the RefRange will always have the exact same elements. Any operation done on one will affect the other. So, for instance, if it's passed to a function which would implicitly copy the original range if it were passed to it, the original range is not copied but is consumed as if it were a reference type.
save works as normal and operates on a new range, so if
save is ever called on the RefRange, then no operations on the saved range will affect the original.
range | the range to construct the RefRange from |
RefRange. If the given range is a class type
(and thus is already a reference type), then the original range is returned rather than a RefRange.
R * _rangeauto retro(Range)(Range r) if (isBidirectionalRange!(Unqual!Range))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.auto stride(Range)(Range r, size_t n) if (isInputRange!(Unqual!Range))Iterates range `r` with stride `n`. If the range is a random-access range, moves by indexing into the range; otherwise, moves by successive calls to `popFront`. Applying stride twice to the same ra...auto chain(Ranges...)(Ranges rs) if (Ranges.length > 0 &&
allSatisfy!(isInputRange, staticMap!(Unqual, Ranges)) &&
!is(CommonType!(staticMap!(ElementType, staticMap!(Unqual, Ranges))) == void))Spans multiple ranges in sequence. The function `chain` takes any number of ranges and returns a Chain!(R1) object. The ranges may be different, but they must have the same element type. The result...auto choose(R1, R2)(bool condition, return scope R1 r1, return scope R2 r2) if (isInputRange!(Unqual!R1) && isInputRange!(Unqual!R2) &&
!is(CommonType!(ElementType!(Unqual!R1), ElementType!(Unqual!R2)) == void))Choose one of two ranges at runtime depending on a Boolean condition.auto chooseAmong(Ranges...)(size_t index, return scope Ranges rs) if (Ranges.length >= 2
&& allSatisfy!(isInputRange, staticMap!(Unqual, Ranges))
&& !is(CommonType!(staticMap!(ElementType, Ranges)) == void))Choose one of multiple ranges at runtime.auto roundRobin(Rs...)(Rs rs) if (Rs.length > 1 && allSatisfy!(isInputRange, staticMap!(Unqual, Rs)))roundRobin(r1) 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 alternat...auto radial(Range, I)(Range r, I startingIndex) if (isRandomAccessRange!(Unqual!Range) && hasLength!(Unqual!Range) && hasSlicing!(Unqual!Range) && isIntegral!I)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. I...auto radial(R)(R r) if (isRandomAccessRange!(Unqual!R) && hasLength!(Unqual!R) && hasSlicing!(Unqual!R))DittoTake!R take(R)(R input, size_t n) if (isInputRange!(Unqual!R))Lazily takes only up to `n` elements of a range. This is particularly useful when using with infinite ranges.auto takeExactly(R)(R range, size_t n) if (isInputRange!R)Similar to take, but assumes that `range` has at least n elements. Consequently, the result of takeExactly(range) always defines the `length` property (and initializes it to `n`) even when `range` ...auto takeOne(R)(R source) if (isInputRange!R)Returns a range with at most one element; for example, takeOne([42) returns a range consisting of the integer 42. Calling `popFront()` off that range renders it empty.auto takeNone(R)() if (isInputRange!R)Returns an empty range which is statically known to be empty and is guaranteed to have `length` and be random access regardless of `R`'s capabilities.auto takeNone(R)(R range) if (isInputRange!R)Creates an empty range from the given range in 1. If it can, it will return the same range type. If not, it will return takeExactly(range).auto tail(Range)(Range range, size_t n) if (isInputRange!Range && !isInfinite!Range &&
(hasLength!Range || isForwardRange!Range))Return a range advanced to within `n` elements of the end of `range`.R drop(R)(R range, size_t n) if (isInputRange!R)`drop` is a convenience function which calls popFrontN`(range, n)` and returns `range`. Unlike `popFrontN`, the range argument is passed by copy, not by `ref`.R dropExactly(R)(R range, size_t n) if (isInputRange!R)Similar to drop and `dropBack` but they call range.popFrontExactly(n) and `range.popBackExactly(n)` instead.R dropOne(R)(R range) if (isInputRange!R)`dropOne` is a convenience function which calls `range.popFront()` and returns `range`. Unlike `popFront`, the range argument is passed by copy, not by `ref`.auto generate(Fun)(Fun fun) if (isCallable!fun)Given callable (isCallable) `fun`, create as a range whose front is defined by successive calls to `fun()`. This is especially useful to call function with global side effects (random functions), o...auto zip(Ranges...)(StoppingPolicy sp, Ranges ranges) if (Ranges.length && allSatisfy!(isInputRange, Ranges))DittoLockstep!(Ranges) lockstep(Ranges...)(Ranges ranges, StoppingPolicy s) if (allSatisfy!(isInputRange, Ranges))DittoRecurrence!(fun, CommonType!(State), State.length) recurrence(alias fun, State...)(State initial)Dittoauto iota(B, E, S)(B begin, E end, S step) if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E)))
&& isIntegral!S)Creates a range of values that span the given starting and stopping values.auto iota(B, E)(B begin, E end) if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E)))Dittoauto iota(B, E)(B begin, E end) if (!isIntegral!(CommonType!(B, E)) &&
!isFloatingPoint!(CommonType!(B, E)) &&
!isPointer!(CommonType!(B, E)) &&
is(typeof((ref B b) { ++ b; })) &&
(is(typeof(B.init < E.init)) || is(typeof(B.init == E.init))) )dittoFrontTransversal!(RangeOfRanges, opt) frontTransversal(
TransverseOptions opt = TransverseOptions.assumeJagged,
RangeOfRanges)(RangeOfRanges rr)DittoTransversal!(RangeOfRanges, opt) transversal(TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges)(RangeOfRanges rr, size_t n)DittoTransposed!(RangeOfRanges, opt) transposed(TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges)(RangeOfRanges rr) if (isForwardRange!RangeOfRanges &&
isInputRange!(ElementType!RangeOfRanges) &&
hasAssignableElements!RangeOfRanges)Given a range of ranges, returns a range of ranges where the i'th subrange contains the i'th elements of the original subranges.EvenChunks!Source evenChunks(Source)(Source source, size_t chunkCount) if (isForwardRange!Source && hasLength!Source)Dittoauto slide(Flag!"withPartial" f = Yes.withPartial,
Source)(Source source, size_t windowSize, size_t stepSize = 1) if (isForwardRange!Source)A fixed-sized sliding window iteration of size `windowSize` over a `source` range by a custom `stepSize`.auto only(Values...)(return scope Values values) if (!is(CommonType!Values == void))Assemble `values` into a range that carries all its elements in-situ.auto enumerate(Enumerator = size_t, Range)(Range range, Enumerator start = 0) if (isIntegral!Enumerator && isInputRange!Range)Iterate over `range` with an attached index variable.auto assumeSorted(alias pred = "a < b", R)(R r) if (isInputRange!(Unqual!R))Assumes `r` is sorted by predicate `pred` and returns the corresponding SortedRange!(pred) having `r` as support. To check for sorted-ness at cost n, use isSorted.auto bitwise(R)(auto ref R range) if (isInputRange!R && isIntegral!(ElementType!R))Bitwise adapter over an integral type range. Consumes the range elements bit by bit, from the least significant bit to the most significant bit.auto tee(Flag!"pipeOnPop" pipeOnPop = Yes.pipeOnPop, R1, R2)(R1 inputRange, R2 outputRange) if (isInputRange!R1 && isOutputRange!(R2, ElementType!R1))Implements a "tee" style pipe, wrapping an input range so that elements of the range can be passed to a provided function or OutputRange as they are iterated over. This is useful for printing out i...auto tee(alias fun, Flag!"pipeOnPop" pipeOnPop = Yes.pipeOnPop, R1)(R1 inputRange) if (is(typeof(fun) == void) || isSomeFunction!fun)Dittoauto padLeft(R, E)(R r, E e, size_t n) if (
((isInputRange!R && hasLength!R) || isForwardRange!R) &&
!is(CommonType!(ElementType!R, E) == void)
)Extends the length of the input range `r` by padding out the start of the range with the element `e`. The element `e` must be of a common type with the element type of the range `r` as defined by C...auto padRight(R, E)(R r, E e, size_t n) if (
isInputRange!R &&
!isInfinite!R &&
!is(CommonType!(ElementType!R, E) == void))Extend the length of the input range `r` by padding out the end of the range with the element `e`. The element `e` must be of a common type with the element type of the range `r` as defined by Comm...if (isInputRange!(Unqual!R) &&
((!isInfinite!(Unqual!R) && hasSlicing!(Unqual!R)) || is(R T == Take!T)))ditto
if (Ranges.length && __traits(compiles,
{
static assert(allSatisfy!(isInputRange, Ranges));
}))Returns true if fn accepts variables of type T1 and T2 in any order. The following code should compile:
(ref T1 a, ref T2 b)
{
fn(a, b);
fn(b, a);
}if (isInstanceOf!(SortedRange, Range))ditto
This simplifies a commonly used idiom in phobos for accepting any kind of string parameter. The type R can for example be a simple string, chained string using
chain, chainPath or any other input range of
characters.
Only finite length character ranges are allowed with this constraint.
This template is equivalent to:
isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R)