std.range
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 std.algorithm.find works not just for arrays, but for linked-lists, input
files, incoming network data, etc. See also Ali Çehreli's
tutorial on ranges for the basics
of working with and creating range-based code.
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:| chain | Concatenates several ranges into a single range. |
| choose | Chooses one of two ranges at runtime based on a boolean condition. |
| chooseAmong | Chooses one of several ranges at runtime based on an index. |
| chunks | Creates a range that returns fixed-size chunks of the original range. |
| cycle | Creates an infinite range that repeats the given forward range indefinitely. Good for implementing circular buffers. |
| drop | Creates the range that results from discarding the first n elements from the given range. |
| dropExactly | Creates the range that results from discarding exactly n of the first elements from the given range. |
| dropOne | Creates the range that results from discarding the first elements from the given range. |
| enumerate | Iterates a range with an attached index variable. |
| evenChunks | Creates a range that returns a number of chunks of approximately equal length from the original range. |
| frontTransversal | Creates a range that iterates over the first elements of the given ranges. |
| indexed | Creates a range that offers a view of a given range as though its elements were reordered according to a given range of indices. |
| iota | Creates a range consisting of numbers between a starting point and ending point, spaced apart by a given interval. |
| lockstep | Iterates n ranges in lockstep, for use in a foreach loop. Similar to zip, except that lockstep is designed especially for foreach loops. |
| NullSink | An output range that discards the data it receives. |
| only | Creates a range that iterates over the given arguments. |
| padLeft | Pads a range to a specified length by adding a given element to
the front of the range. Is lazy if the range has a known length.
|
| padRight | Lazily pads a range to a specified length by adding a given element to the back of the range. |
| radial | Given a random-access range and a starting point, creates a range that alternately returns the next left and next right element to the starting point. |
| recurrence | Creates a forward range whose values are defined by a mathematical recurrence relation. |
| repeat | Creates a range that consists of a single element repeated n times, or an infinite range repeating that element indefinitely. |
| retro | Iterates a bidirectional range backwards. |
| roundRobin | Given n ranges, creates a new range that return the n first elements of each range, in turn, then the second element of each range, and so on, in a round-robin fashion. |
| sequence | Similar to recurrence, except that a random-access range is created. |
| stride | Iterates a range with stride n. |
| tail | Return a range advanced to within n elements of the end of the given range. |
| take | Creates a sub-range consisting of only up to the first n elements of the given range. |
| takeExactly | Like take, but assumes the given range actually has n elements, and therefore also defines the length property. |
| takeNone | Creates a random-access range consisting of zero elements of the given range. |
| takeOne | Creates a random-access range consisting of exactly the first element of the given range. |
| tee | Creates a range that wraps a given range, forwarding along its elements while also calling a provided function with each element. |
| transposed | Transposes a range of ranges. |
| transversal | Creates a range that iterates over the n'th elements of the given random-access ranges. |
| zip | Given n ranges, creates a range that successively returns a tuple of all the first elements, a tuple of all the second elements, etc. |
Source: std/range/package.d
- auto
retro(Range)(Ranger)
if (isBidirectionalRange!(Unqual!Range)); - Iterates a bidirectional range backwards. The original range can be accessed by using the source property. Applying
retrotwice to the same range yields the original range.Parameters:Range rthe bidirectional range to iterate backwards Returns:A bidirectional range with length ifralso provides a length. Or, ifris a random access range, then the return value will be random access as well.See Also:std.algorithm.mutation.reverse for mutating the source range directly.Examples:import std.algorithm.comparison : equal; int[5] a = [ 1, 2, 3, 4, 5 ]; int[5] b = [ 5, 4, 3, 2, 1 ]; assert(equal(retro(a[]), b[])); assert(retro(a[]).source is a[]); assert(retro(retro(a[])) is a[]);
- auto
stride(Range)(Ranger, size_tn)
if (isInputRange!(Unqual!Range)); - Iterates range
rwithstriden. If the range is a random-access range, moves by indexing into the range; otherwise, moves by successive calls to popFront. Applyingstridetwice to the same range results in astridewith a step that is the product of the two applications. It is an error fornto be 0.Parameters:Range rthe input range to strideoversize_t nthe number of elements to skip over Returns:At minimum, an input range. The resulting range will adopt the range primitives of the underlying range as long as hasLength.std.range.primitives is true.Examples:import std.algorithm.comparison : equal; int[] a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]; assert(equal(stride(a, 3), [ 1, 4, 7, 10 ][])); writeln(stride(stride(a, 2), 3)); // stride(a, 6)
- auto
chain(Ranges...)(Rangesrs)
if (Ranges.length > 0 && allSatisfy!(isInputRange, staticMap!(Unqual, Ranges)) && !is(CommonType!(staticMap!(ElementType, staticMap!(Unqual, Ranges))) == void)); - Spans multiple ranges in sequence. The function
chaintakes 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 orchain, the Chain type exits the picture by aliasing itself directly to that range's type.Parameters:Ranges rsthe input ranges to chaintogetherReturns:An input range at minimum. If all of the ranges inrsprovide a range primitive, the returned range will also provide that range primitive.See Also:only tochainvalues to a rangeExamples:import std.algorithm.comparison : equal; int[] arr1 = [ 1, 2, 3, 4 ]; int[] arr2 = [ 5, 6 ]; int[] arr3 = [ 7 ]; auto s = chain(arr1, arr2, arr3); writeln(s.length); // 7 writeln(s[5]); // 6 assert(equal(s, [1, 2, 3, 4, 5, 6, 7][]));
Examples:Range primitives are carried over to the returned range if all of the ranges provide themimport std.algorithm.sorting : sort; import std.algorithm.comparison : equal; int[] arr1 = [5, 2, 8]; int[] arr2 = [3, 7, 9]; int[] arr3 = [1, 4, 6]; // in-place sorting across all of the arrays auto s = arr1.chain(arr2, arr3).sort; assert(s.equal([1, 2, 3, 4, 5, 6, 7, 8, 9])); assert(arr1.equal([1, 2, 3])); assert(arr2.equal([4, 5, 6])); assert(arr3.equal([7, 8, 9]));
- auto
choose(R1, R2)(boolcondition, R1r1, R2r2)
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.The ranges may be different, but they must have compatible element types (i.e. CommonType must exist for the two element types). The result is a range that offers the weakest capabilities of the two (e.g. ForwardRange if R1 is a random-access range and R2 is a forward range).Parameters:bool conditionwhich range to choose:r1iftrue,r2otherwiseR1 r1the " true" rangeR2 r2the " false" rangeReturns:A range type dependent on R1 and R2.Bugs:Examples:import std.algorithm.comparison : equal; import std.algorithm.iteration : filter, map; auto data1 = [ 1, 2, 3, 4 ].filter!(a => a != 3); auto data2 = [ 5, 6, 7, 8 ].map!(a => a + 1); // choose() is primarily useful when you need to select one of two ranges // with different types at runtime. static assert(!is(typeof(data1) == typeof(data2))); auto chooseRange(bool pickFirst) { // The returned range is a common wrapper type that can be used for // returning or storing either range without running into a type error. return choose(pickFirst, data1, data2); // Simply returning the chosen range without using choose() does not // work, because map() and filter() return different types. //return pickFirst ? data1 : data2; // does not compile } auto result = chooseRange(true); assert(result.equal([ 1, 2, 4 ])); result = chooseRange(false); assert(result.equal([ 6, 7, 8, 9 ]));
- auto
chooseAmong(Ranges...)(size_tindex, Rangesrs)
if (Ranges.length > 2 && is(typeof(choose(true,rs[0],rs[1]))) && is(typeof(chooseAmong(0,rs[1..$]))));
autochooseAmong(Ranges...)(size_tindex, Rangesrs)
if (Ranges.length == 2 && is(typeof(choose(true,rs[0],rs[1])))); - Choose one of multiple ranges at runtime.The ranges may be different, but they must have compatible element types. The result is a range that offers the weakest capabilities of all Ranges.Parameters:
size_t indexwhich range to choose, must be less than the number of ranges Ranges rstwo or more ranges Returns:The indexed range. Ifrsconsists of only one range, the return type is an alias of that range's type.Examples:import std.algorithm.comparison : equal; int[] arr1 = [ 1, 2, 3, 4 ]; int[] arr2 = [ 5, 6 ]; int[] arr3 = [ 7 ]; { auto s = chooseAmong(0, arr1, arr2, arr3); auto t = s.save; writeln(s.length); // 4 writeln(s[2]); // 3 s.popFront(); assert(equal(t, [1, 2, 3, 4][])); } { auto s = chooseAmong(1, arr1, arr2, arr3); writeln(s.length); // 2 s.front = 8; assert(equal(s, [8, 6][])); } { auto s = chooseAmong(1, arr1, arr2, arr3); writeln(s.length); // 2 s[1] = 9; assert(equal(s, [8, 9][])); } { auto s = chooseAmong(1, arr2, arr1, arr3)[1 .. 3]; writeln(s.length); // 2 assert(equal(s, [2, 3][])); } { auto s = chooseAmong(0, arr1, arr2, arr3); writeln(s.length); // 4 writeln(s.back); // 4 s.popBack(); s.back = 5; assert(equal(s, [1, 2, 5][])); s.back = 3; assert(equal(s, [1, 2, 3][])); } { uint[] foo = [1,2,3,4,5]; uint[] bar = [6,7,8,9,10]; auto c = chooseAmong(1,foo, bar); writeln(c[3]); // 9 c[3] = 42; writeln(c[3]); // 42 writeln(c.moveFront()); // 6 writeln(c.moveBack()); // 10 writeln(c.moveAt(4)); // 10 } { import std.range : cycle; auto s = chooseAmong(1, cycle(arr2), cycle(arr3)); assert(isInfinite!(typeof(s))); assert(!s.empty); writeln(s[100]); // 7 }
- auto
roundRobin(Rs...)(Rsrs)
if (Rs.length > 1 && allSatisfy!(isInputRange, staticMap!(Unqual, Rs))); 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.roundRobinstops after it has consumed all ranges (skipping over the ones that finish early).Examples:import std.algorithm.comparison : equal; int[] a = [ 1, 2, 3 ]; int[] b = [ 10, 20, 30, 40 ]; auto r = roundRobin(a, b); assert(equal(r, [ 1, 10, 2, 20, 3, 30, 40 ]));
Examples:roundRobincan be used to create "interleave" functionality which inserts an element between each element in a range.import std.algorithm.comparison : equal; auto interleave(R, E)(R range, E element) if ((isInputRange!R && hasLength!R) || isForwardRange!R) { static if (hasLength!R) immutable len = range.length; else immutable len = range.save.walkLength; return roundRobin( range, element.repeat(len - 1) ); } assert(interleave([1, 2, 3], 0).equal([1, 0, 2, 0, 3]));
- auto
radial(Range, I)(Ranger, IstartingIndex)
if (isRandomAccessRange!(Unqual!Range) && hasLength!(Unqual!Range) && hasSlicing!(Unqual!Range) && isIntegral!I);
autoradial(R)(Rr)
if (isRandomAccessRange!(Unqual!R) && hasLength!(Unqual!R) && hasSlicing!(Unqual!R)); - 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.When
startingIndexis 0 the range will be fully iterated in order and in reverse order whenr.length is given.Parameters:Range ra random access range with length and slicing I startingIndexthe index to begin iteration from Returns:A forward range with lengthExamples:import std.algorithm.comparison : equal; 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 ])); // If the left end is reached first, the remaining elements on the right // are concatenated in order: a = [ 0, 1, 2, 3, 4, 5 ]; assert(equal(radial(a, 1), [ 1, 2, 0, 3, 4, 5 ])); // If the right end is reached first, the remaining elements on the left // are concatenated in reverse order: assert(equal(radial(a, 4), [ 4, 5, 3, 2, 1, 0 ]));
- Take!R
take(R)(Rinput, size_tn)
if (isInputRange!(Unqual!R) && !isInfinite!(Unqual!R) && hasSlicing!(Unqual!R) && !is(R T == Take!T));
structTake(Range) if (isInputRange!(Unqual!Range) && !(!isInfinite!(Unqual!Range) && hasSlicing!(Unqual!Range) || is(Range T ==Take!T))); - Lazily takes only up to
nelements of a range. This is particularly useful when using with infinite ranges.Unlike takeExactly,takedoes not require that there arenor more elements ininput. As a consequence, length information is not applied to the result unlessinputalso has length information.Parameters:R inputan inputrange to iterate over up tontimessize_t nthe number of elements to takeReturns:At minimum, aninputrange. If the range offers random access and length,takeoffers them as well. - template
Take(R) if (isInputRange!(Unqual!R) && (!isInfinite!(Unqual!R) && hasSlicing!(Unqual!R) || is(R T ==Take!T)))
Take!Rtake(R)(Rinput, size_tn)
if (is(R T == Take!T));
Take!Rtake(R)(Rinput, size_tn)
if (isInputRange!(Unqual!R) && (isInfinite!(Unqual!R) || !hasSlicing!(Unqual!R) && !is(R T == Take!T))); - This template simply aliases itself to R and is useful for consistency in generic code.Examples:
import std.algorithm.comparison : equal; int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; auto s = take(arr1, 5); writeln(s.length); // 5 writeln(s[4]); // 5 assert(equal(s, [ 1, 2, 3, 4, 5 ][]));
Examples:If the range runs out beforenelements,takesimply returns the entire range (unlike takeExactly, which will cause an assertion failure if the range ends prematurely):import std.algorithm.comparison : equal; int[] arr2 = [ 1, 2, 3 ]; auto t = take(arr2, 5); writeln(t.length); // 3 assert(equal(t, [ 1, 2, 3 ]));
- auto
takeExactly(R)(Rrange, size_tn)
if (isInputRange!R); - Similar to take, but assumes that
rangehas at leastnelements. Consequently, the result oftakeExactly(range,n) always defines the length property (and initializes it ton) even whenrangeitself does not define length.The result oftakeExactlyis identical to that of take in cases where the originalrangedefines length or is infinite. Unlike take, however, it is illegal to pass arangewith less thannelements totakeExactly; this will cause an assertion failure.Examples:import std.algorithm.comparison : equal; auto a = [ 1, 2, 3, 4, 5 ]; auto b = takeExactly(a, 3); assert(equal(b, [1, 2, 3])); static assert(is(typeof(b.length) == size_t)); writeln(b.length); // 3 writeln(b.front); // 1 writeln(b.back); // 3
- auto
takeOne(R)(Rsource)
if (isInputRange!R); - 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.In effecttakeOne(r) is somewhat equivalent to take(r, 1) but in certain interfaces it is important to know statically that the range may only have at most one element. The type returned bytakeOneis a random-access range with length regardless of R's capabilities, as long as it is a forward range. (another feature that distinguishestakeOnefrom take). If (D R) is an input range but not a forward range, return type is an input range with all random-access capabilites except save.Examples:auto s = takeOne([42, 43, 44]); static assert(isRandomAccessRange!(typeof(s))); writeln(s.length); // 1 assert(!s.empty); writeln(s.front); // 42 s.front = 43; writeln(s.front); // 43 writeln(s.back); // 43 writeln(s[0]); // 43 s.popFront(); writeln(s.length); // 0 assert(s.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.Examples:
auto range = takeNone!(int[])(); writeln(range.length); // 0 assert(range.empty);
- auto
takeNone(R)(Rrange)
if (isInputRange!R); - Creates an empty
rangefrom the givenrangein Ο(1). If it can, it will return the samerangetype. If not, it will return takeExactly(range, 0).Examples:import std.algorithm.iteration : filter; assert(takeNone([42, 27, 19]).empty); assert(takeNone("dlang.org").empty); assert(takeNone(filter!"true"([42, 27, 19])).empty);
- auto
tail(Range)(Rangerange, size_tn)
if (isInputRange!Range && !isInfinite!Range && (hasLength!Range || isForwardRange!Range)); - Return a range advanced to within n elements of the end of range.Intended as the range equivalent of the Unix tail utility. When the length of range is less than or equal to n, range is returned as-is. Completes in Ο(1) steps for ranges that support slicing and have length. Completes in Ο(range.length) time for all other ranges.Parameters:
Range rangerange to get tail of size_t nmaximum number of elements to include in tail Returns:Returns the tail of range augmented with length informationExamples:// tail -c n writeln([1, 2, 3].tail(1)); // [3] writeln([1, 2, 3].tail(2)); // [2, 3] writeln([1, 2, 3].tail(3)); // [1, 2, 3] writeln([1, 2, 3].tail(4)); // [1, 2, 3] writeln([1, 2, 3].tail(0).length); // 0 // tail --lines=n import std.algorithm.comparison : equal; import std.algorithm.iteration : joiner; import std.string : lineSplitter; import std.exception : assumeWontThrow; assert("one\ntwo\nthree" .lineSplitter .tail(2) .joiner("\n") .equal("two\nthree") .assumeWontThrow);
- R
drop(R)(Rrange, size_tn)
if (isInputRange!R);
RdropBack(R)(Rrange, size_tn)
if (isBidirectionalRange!R); - Convenience function which calls
range.range_primitives.html#.popFrontN">std.range.primitives.popFrontN(n) and returnsrange`.dropmakes it easier to pop elements from arangeand then pass it to another function within a single expression, whereas popFrontN would require multiple statements.dropBackprovides the same functionality but instead calls `range.std.range.primitives.popBackN(n)Note:
dropanddropBackwill only pop up tonelements but will stop if therangeis empty first. In other languages this is sometimes called skip.Parameters:R rangethe input rangetodropfromsize_t nthe number of elements to dropReturns:rangewith up tonelements droppedSee Also:Examples:import std.algorithm.comparison : equal; writeln([0, 2, 1, 5, 0, 3].drop(3)); // [5, 0, 3] writeln("hello world".drop(6)); // "world" assert("hello world".drop(50).empty); assert("hello world".take(6).drop(3).equal("lo "));
- R
dropExactly(R)(Rrange, size_tn)
if (isInputRange!R);
RdropBackExactly(R)(Rrange, size_tn)
if (isBidirectionalRange!R); - Similar to drop and dropBack but they call
range.popFrontExactly(n) andrange.popBackExactly(n) instead.Note: Unlike drop,
dropExactlywill assume that therangeholds at leastnelements. This makesdropExactlyfaster than drop, but it also means that ifrangedoes not contain at leastnelements, it will attempt to call popFront on an emptyrange, which is undefined behavior. So, only use popFrontExactly when it is guaranteed thatrangeholds at leastnelements.Parameters:R rangethe input rangeto drop fromsize_t nthe number of elements to drop Returns:rangewithnelements droppedSee Also:Examples:import std.algorithm.comparison : equal; import std.algorithm.iteration : filterBidirectional; auto a = [1, 2, 3]; writeln(a.dropExactly(2)); // [3] writeln(a.dropBackExactly(2)); // [1] string s = "日本語"; writeln(s.dropExactly(2)); // "語" writeln(s.dropBackExactly(2)); // "日" auto bd = filterBidirectional!"true"([1, 2, 3]); assert(bd.dropExactly(2).equal([3])); assert(bd.dropBackExactly(2).equal([1]));
- R
dropOne(R)(Rrange)
if (isInputRange!R);
RdropBackOne(R)(Rrange)
if (isBidirectionalRange!R); - Convenience function which calls
range.popFront() and returnsrange.dropOnemakes it easier to pop an element from arangeand then pass it to another function within a single expression, whereas popFront would require multiple statements.dropBackOneprovides the same functionality but instead callsrange.popBack().Examples:import std.algorithm.comparison : equal; import std.algorithm.iteration : filterBidirectional; import std.container.dlist : DList; auto dl = DList!int(9, 1, 2, 3, 9); assert(dl[].dropOne().dropBackOne().equal([1, 2, 3])); auto a = [1, 2, 3]; writeln(a.dropOne()); // [2, 3] writeln(a.dropBackOne()); // [1, 2] string s = "日本語"; import std.exception : assumeWontThrow; writeln(s.dropOne()); // "本語" writeln(s.dropBackOne()); // "日本" auto bd = filterBidirectional!"true"([1, 2, 3]); assert(bd.dropOne().equal([2, 3])); assert(bd.dropBackOne().equal([1, 2]));
- struct
Repeat(T);
Repeat!Trepeat(T)(Tvalue); - Create a range which repeats one
valueforever.Parameters:T valuethe valuetorepeatReturns:An infinite random access range with slicing.Examples:import std.algorithm.comparison : equal; assert(equal(5.repeat().take(4), [ 5, 5, 5, 5 ]));
- inout @property inout(T)
front();
inout @property inout(T)back();
enum boolempty;
voidpopFront();
voidpopBack();
inout @property autosave();
inout inout(T)opIndex(size_t);
autoopSlice(size_ti, size_tj);
enum autoopDollar;
inout autoopSlice(size_t, DollarToken); - Range primitives
- Take!(Repeat!T)
repeat(T)(Tvalue, size_tn); - Repeats
valueexactlyntimes. Equivalent to take(repeat(value),n).Examples:import std.algorithm.comparison : equal; assert(equal(5.repeat(4), 5.repeat().take(4)));
- auto
generate(Fun)(Funfun)
if (isCallable!fun);
autogenerate(alias fun)()
if (isCallable!fun); - Given callable (std.traits.isCallable)
fun, create as a range whose front is defined by successive calls tofun(). This is especially useful to call function with global side effects (random functions), or to create ranges expressed as a single delegate, rather than an entire front/popFront/empty structure.funmaybe be passed either a template alias parameter (existing function, delegate, struct type defining static opCall) or a run-time value argument (delegate, function object). The result range models an InputRange (std.range.primitives.isInputRange). The resulting range will callfun() on construction, and every call to popFront, and the cached value will be returned when front is called.Returns:an inputRange where each element represents another call tofun.Examples:import std.algorithm.comparison : equal; import std.algorithm.iteration : map; int i = 1; auto powersOfTwo = generate!(() => i *= 2)().take(10); assert(equal(powersOfTwo, iota(1, 11).map!"2^^a"()));
Examples:import std.algorithm.comparison : equal; //Returns a run-time delegate auto infiniteIota(T)(T low, T high) { T i = high; return (){if (i == high) i = low; return i++;}; } //adapted as a range. assert(equal(generate(infiniteIota(1, 4)).take(10), [1, 2, 3, 1, 2, 3, 1, 2, 3, 1]));
Examples:import std.format : format; import std.random : uniform; auto r = generate!(() => uniform(0, 6)).take(10); format("%(%s %)", r);
- struct
Cycle(R) if (isForwardRange!R && !isInfinite!R);
templateCycle(R) if (isInfinite!R) - Repeats the given forward range ad infinitum. If the original range is infinite (fact that would make
Cyclethe identity application),Cycledetects that and aliases itself to the range type itself. If the original range has random access,Cycleoffers random access and also offers a constructor taking an initial position index.Cycleworks with static arrays in addition to ranges, mostly for performance reasons.Note: The input range must not be empty.
Tip: This is a great way to implement simple circular buffers.
- this(R
input, size_tindex= 0);
@property ref autofront();
const @property ref autofront();
@property voidfront(ElementType!Rval);
enum boolempty;
voidpopFront();
ref autoopIndex(size_tn);
const ref autoopIndex(size_tn);
voidopIndexAssign(ElementType!Rval, size_tn);
@property Cyclesave();
enum autoopDollar;
autoopSlice(size_ti, size_tj);
autoopSlice(size_ti, DollarToken); - Range primitives
- struct
Cycle(R) if (isStaticArray!R);
Cycle!Rcycle(R)(Rinput)
if (isForwardRange!R && !isInfinite!R);
Cycle!Rcycle(R)(Rinput, size_tindex= 0)
if (isRandomAccessRange!R && !isInfinite!R);
Cycle!Rcycle(R)(Rinput)
if (isInfinite!R);
@system Cycle!Rcycle(R)(ref Rinput, size_tindex= 0)
if (isStaticArray!R); - Examples:
import std.algorithm.comparison : equal; import std.range : cycle, take; // Here we create an infinitive cyclic sequence from [1, 2] // (i.e. get here [1, 2, 1, 2, 1, 2 and so on]) then // take 5 elements of this sequence (so we have [1, 2, 1, 2, 1]) // and compare them with the expected values for equality. assert(cycle([1, 2]).take(5).equal([ 1, 2, 1, 2, 1 ]));
- @system this(ref R
input, size_tindex= 0);
inout @property ref @safe inout(ElementType)front();
enum boolempty;
@safe voidpopFront();
inout ref @safe inout(ElementType)opIndex(size_tn);
inout @property @safe inout(Cycle)save();
enum autoopDollar;
@safe autoopSlice(size_ti, size_tj);
inout @safe inout(typeof(this))opSlice(size_ti, DollarToken); - Range primitives
- struct
Zip(Ranges...) if (Ranges.length && allSatisfy!(isInputRange, Ranges));
autozip(Ranges...)(Rangesranges)
if (Ranges.length && allSatisfy!(isInputRange, Ranges));
autozip(Ranges...)(StoppingPolicysp, Rangesranges)
if (Ranges.length && allSatisfy!(isInputRange, Ranges)); - Iterate several
rangesin lockstep. The element type is a proxy tuple that allows accessing the current element in the nth range by using e[n].zipis 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.Parameters:StoppingPolicy spcontrols what zipwill do if the ranges are different lengthsRanges rangesthe rangestoziptogetherReturns:At minimum, an input range.Zipoffers the lowest range facilities of all components, e.g. it offers random access iff allrangesoffer random access, and also offers mutation and swapping if allrangesoffer it. Due to this,Zipis extremely powerful because it allows manipulating severalrangesin lockstep.Throws:An Exception if all of the ranges are not the same length andspis set to StoppingPolicy.requireSameLength.Examples:import std.algorithm.comparison : equal; import std.algorithm.iteration : map; // pairwise sum auto arr = [0, 1, 2]; assert(zip(arr, arr.dropOne).map!"a[0] + a[1]".equal([1, 3]));
Examples:import std.conv : to; int[] a = [ 1, 2, 3 ]; string[] b = [ "a", "b", "c" ]; string[] result; foreach (tup; zip(a, b)) { result ~= tup[0].to!string ~ tup[1]; } writeln(result); // ["1a", "2b", "3c"] size_t idx = 0; // unpacking tuple elements with foreach foreach (e1, e2; zip(a, b)) { writeln(e1); // a[idx] writeln(e2); // b[idx] ++idx; }
Examples:zipis powerful - the following code sorts two arrays in parallel:import std.algorithm.sorting : sort; int[] a = [ 1, 2, 3 ]; string[] b = [ "a", "c", "b" ]; zip(a, b).sort!((t1, t2) => t1[0] > t2[0]); writeln(a); // [3, 2, 1] // b is sorted according to a's sorting writeln(b); // ["b", "c", "a"]
- this(R
rs, StoppingPolicys= StoppingPolicy.shortest); - Builds an object. Usually this is invoked indirectly by using the zip function.
- enum bool
empty; - Returns
trueif the range is at end. The test depends on the stopping policy. - @property Zip
save(); - @property ElementType
front(); - Returns the current iterated element.
- @property void
front(ElementTypev); - Sets the
frontof all iterated ranges. - ElementType
moveFront(); - Moves out the front.
- @property ElementType
back(); - Returns the rightmost element.
- ElementType
moveBack(); - Moves out the back.Returns the rightmost element.
- @property void
back(ElementTypev); - Returns the current iterated element.Returns the rightmost element.
- void
popFront(); - Advances to the next element in all controlled ranges.
- void
popBack(); - Calls
popBackfor all controlled ranges. - @property auto
length(); - Returns the
lengthof this range. Defined only if all ranges definelength. - alias
opDollar= length; - Returns the length of this range. Defined only if all ranges define length.
- auto
opSlice(size_tfrom, size_tto); - Returns a slice of the range. Defined only if all range define slicing.
- ElementType
opIndex(size_tn); - Returns the
nth element in the composite range. Defined if all ranges offer random access. - void
opIndexAssign(ElementTypev, size_tn); - Assigns to the
nth element in the composite range. Defined if all ranges offer random access.Returns thenth element in the composite range. Defined if all ranges offer random access. - ElementType
moveAt(size_tn); - Destructively reads the
nth element in the composite range. Defined if all ranges offer random access.Returns thenth element in the composite range. Defined if all ranges offer random access.
- enum
StoppingPolicy: int; - Dictates how iteration in a Zip should stop. By default stop at the end of the shortest of all ranges.
shortest- Stop when the
shortestrange is exhausted longest- Stop when the
longestrange is exhausted requireSameLength- Require that all ranges are equal
- struct
Lockstep(Ranges...) if (Ranges.length > 1 && allSatisfy!(isInputRange, Ranges));
Lockstep!Rangeslockstep(Ranges...)(Rangesranges)
if (allSatisfy!(isInputRange, Ranges));
Lockstep!Rangeslockstep(Ranges...)(Rangesranges, StoppingPolicys)
if (allSatisfy!(isInputRange, Ranges)); - Iterate multiple
rangesinlockstepusing a foreach loop. In contrast to zip it allows reference access to its elements. If only a single range is passed in, theLockstepaliases itself away. If therangesare of different lengths ands== StoppingPolicy.shortest stop after the shortest range is empty. If therangesare of different lengths ands== StoppingPolicy.requireSameLength, throw an exception.smay not be StoppingPolicy.longest, and passing this will throw an exception.Iterating overLockstepin reverse and with an index is only possible whens== StoppingPolicy.requireSameLength, in order to preserve indexes. If an attempt is made at iterating in reverse whens== StoppingPolicy.shortest, an exception will be thrown. By default StoppingPolicy is set to StoppingPolicy.shortest.See Also:Examples:auto arr1 = [1,2,3,4,5,100]; auto arr2 = [6,7,8,9,10]; foreach (ref a, b; lockstep(arr1, arr2)) { a += b; } writeln(arr1); // [7, 9, 11, 13, 15, 100] /// Lockstep also supports iterating with an index variable: foreach (index, a, b; lockstep(arr1, arr2)) { writeln(arr1[index]); // a writeln(arr2[index]); // b }
- this(R
ranges, StoppingPolicysp= StoppingPolicy.shortest);
- struct
Recurrence(alias fun, StateType, size_t stateSize);
Recurrence!(fun, CommonType!State, State.length)recurrence(alias fun, State...)(Stateinitial); - Creates a mathematical sequence given the
initialvalues and arecurrencefunction that computes the next value from the existing values. The sequence comes in the form of an infinite forward range. The typeRecurrenceitself is seldom used directly; most often, recurrences are obtained by calling the functionrecurrence.When callingrecurrence, the function that computes the next value is specified as a template argument, and theinitialvalues in therecurrenceare passed as regular arguments. For example, in a Fibonacci sequence, there are twoinitialvalues (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 therecurrencehas 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 torecurrence. TheRecurrencestruct itself takes care of managing therecurrence's state and shifting it appropriately.Examples:import std.algorithm.comparison : equal; // The Fibonacci numbers, using function in string form: // 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); assert(fib.take(10).equal([1, 1, 2, 3, 5, 8, 13, 21, 34, 55])); // The factorials, using function in lambda form: auto fac = recurrence!((a,n) => a[n-1] * n)(1); assert(take(fac, 10).equal([ 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 ])); // The triangular numbers, using function in explicit form: static size_t genTriangular(R)(R state, size_t n) { return state[n-1] + n; } auto tri = recurrence!genTriangular(0); assert(take(tri, 10).equal([0, 1, 3, 6, 10, 15, 21, 28, 36, 45]));
- struct
Sequence(alias fun, State);
autosequence(alias fun, State...)(Stateargs); Sequenceis 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 bySequenceis a random-access range, as opposed to the regular Recurrence, which only offers forward iteration.The state of thesequenceis stored as a Tuple so it can be heterogeneous.Examples:Odd numbers, using function in string form:auto odds = sequence!("a[0] + n * a[1]")(1, 2); writeln(odds.front); // 1 odds.popFront(); writeln(odds.front); // 3 odds.popFront(); writeln(odds.front); // 5
Examples:Triangular numbers, using function in lambda form:auto tri = sequence!((a,n) => n*(n+1)/2)(); // Note random access writeln(tri[0]); // 0 writeln(tri[3]); // 6 writeln(tri[1]); // 1 writeln(tri[4]); // 10 writeln(tri[2]); // 3
Examples:Fibonacci numbers, using function in explicit form:import std.math : pow, round, sqrt; static ulong computeFib(S)(S state, size_t n) { // Binet's formula return cast(ulong)(round((pow(state[0], n+1) - pow(state[1], n+1)) / state[2])); } auto fib = sequence!computeFib( (1.0 + sqrt(5.0)) / 2.0, // Golden Ratio (1.0 - sqrt(5.0)) / 2.0, // Conjugate of Golden Ratio sqrt(5.0)); // Note random access with [] operator writeln(fib[1]); // 1 writeln(fib[4]); // 5 writeln(fib[3]); // 3 writeln(fib[2]); // 2 writeln(fib[9]); // 55
- auto
iota(B, E, S)(Bbegin, Eend, Sstep)
if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) && isIntegral!S);
autoiota(B, E)(Bbegin, Eend)
if (isFloatingPoint!(CommonType!(B, E)));
autoiota(B, E)(Bbegin, Eend)
if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E)));
autoiota(E)(Eend)
if (is(typeof(iota(E(0),end))));
autoiota(B, E, S)(Bbegin, Eend, Sstep)
if (isFloatingPoint!(CommonType!(B, E, S)));
autoiota(B, E)(Bbegin, Eend)
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)))); - Construct a range of values that span the given starting and stopping values.Parameters:
B beginThe starting value. E endThe value that serves as the stopping criterion. This value is not included in the range. S stepThe value to add to the current value at each iteration. Returns:A range that goes through the numbersbegin,begin+step,begin+ 2 *step, ..., up to and excludingend. The two-argument overloads havestep= 1. Ifbegin<end&&step< 0 orbegin>end&&step> 0 orbegin==end, then an empty range is returned. Ifstep== 0 thenbegin==endis an error. For built-in types, the range returned is a random access range. For user-defined types that support ++, the range is an input range.Example:
void main() { import std.stdio; // The following groups all produce the same output of: // 0 1 2 3 4 foreach (i; 0 .. 5) writef("%s ", i); writeln(); import std.range : iota; foreach (i; iota(0, 5)) writef("%s ", i); writeln(); writefln("%(%s %|%)", iota(0, 5)); import std.algorithm.iteration : map; import std.algorithm.mutation : copy; import std.format; iota(0, 5).map!(i => format("%s ", i)).copy(stdout.lockingTextWriter()); writeln(); }
Examples:import std.algorithm.comparison : equal; import std.math : approxEqual; 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][])); writeln(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]));
- enum
TransverseOptions: int; - Options for the FrontTransversal and Transversal ranges (below).
assumeJagged- When transversed, the elements of a range of ranges are assumed to have different lengths (e.g. a jagged array).
enforceNotJagged- 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.
assumeNotJagged- 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.
- struct
FrontTransversal(Ror, TransverseOptions opt = TransverseOptions.assumeJagged);
FrontTransversal!(RangeOfRanges, opt)frontTransversal(TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges)(RangeOfRangesrr); - Given a range of ranges, iterate transversally through the first elements of each of the enclosed ranges.Examples:
import std.algorithm.comparison : equal; 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. - enum bool
empty;
@property ref autofront();
ElementTypemoveFront();
voidpopFront(); - Forward range primitives.
- @property FrontTransversal
save(); - Duplicates this frontTransversal. Note that only the encapsulating range of range will be duplicated. Underlying ranges will not be duplicated.
- @property ref auto
back();
voidpopBack();
ElementTypemoveBack(); - Bidirectional primitives. They are offered if isBidirectionalRange!RangeOfRanges.
- ref auto
opIndex(size_tn);
ElementTypemoveAt(size_tn);
voidopIndexAssign(ElementTypeval, size_tn);
@property size_tlength();
aliasopDollar= length; - Random-access primitive. It is offered if isRandomAccessRange!RangeOfRanges && (opt == TransverseOptions.assumeNotJagged || opt == TransverseOptions.enforceNotJagged).
- typeof(this)
opSlice(size_tlower, size_tupper); - Slicing if offered if RangeOfRanges supports slicing and all the conditions for supporting indexing are met.
- struct
Transversal(Ror, TransverseOptions opt = TransverseOptions.assumeJagged);
Transversal!(RangeOfRanges, opt)transversal(TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges)(RangeOfRangesrr, size_tn); - Given a range of ranges, iterate transversally through the
nth element of each of the enclosed ranges.Parameters:opt Controls the assumptions the function makes about the lengths of the ranges RangeOfRanges rrAn input range of random access ranges Returns:At minimum, an input range. Range primitives such as bidirectionality and random access are given if the element type ofrrprovides them.Examples:import std.algorithm.comparison : equal; 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_tn); - Construction from an
inputand an index. - enum bool
empty;
@property ref autofront();
EmoveFront();
@property voidfront(Eval);
voidpopFront();
@property typeof(this)save(); - Forward range primitives.
- @property ref auto
back();
voidpopBack();
EmoveBack();
@property voidback(Eval); - Bidirectional primitives. They are offered if isBidirectionalRange!RangeOfRanges.
- ref auto
opIndex(size_tn);
EmoveAt(size_tn);
voidopIndexAssign(Eval, size_tn);
@property size_tlength();
aliasopDollar= length; - Random-access primitive. It is offered if isRandomAccessRange!RangeOfRanges && (opt == TransverseOptions.assumeNotJagged || opt == TransverseOptions.enforceNotJagged).
- typeof(this)
opSlice(size_tlower, size_tupper); - Slicing if offered if RangeOfRanges supports slicing and all the conditions for supporting indexing are met.
- Transposed!RangeOfRanges
transposed(RangeOfRanges)(RangeOfRangesrr)
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.Examples:
import std.algorithm.comparison : equal; int[][] ror = [ [1, 2, 3], [4, 5, 6] ]; auto xp = transposed(ror); assert(equal!"a.equal(b)"(xp, [ [1, 4], [2, 5], [3, 6] ]));
Examples:int[][] x = new int[][2]; x[0] = [1, 2]; x[1] = [3, 4]; auto tr = transposed(x); int[][] witness = [ [ 1, 3 ], [ 2, 4 ] ]; uint i; foreach (e; tr) { writeln(array(e)); // witness[i++] }
- struct
Indexed(Source, Indices) if (isRandomAccessRange!Source && isInputRange!Indices && is(typeof(Source.init[ElementType!Indices.init])));
Indexed!(Source, Indices)indexed(Source, Indices)(Sourcesource, Indicesindices); - This struct takes two ranges,
sourceandindices, and creates a view ofsourceas if its elements were reordered according toindices.indicesmay include only a subset of the elements ofsourceand 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:import std.algorithm.comparison : equal; 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])); assert(equal(retro(ind), [5, 1, 3, 2, 4, 5]));
- @property ref auto
front();
voidpopFront();
@property typeof(this)save();
@property ref autofront(ElementType!SourcenewVal);
automoveFront();
@property ref autoback();
voidpopBack();
@property ref autoback(ElementType!SourcenewVal);
automoveBack();
@property size_tlength();
ref autoopIndex(size_tindex);
typeof(this)opSlice(size_ta, size_tb);
autoopIndexAssign(ElementType!SourcenewVal, size_tindex);
automoveAt(size_tindex); - Range primitives
- @property Source
source(); - Returns the
sourcerange. - @property Indices
indices(); - Returns the
indicesrange. - size_t
physicalIndex(size_tlogicalIndex); - 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]); writeln(ind.physicalIndex(0)); // 1
- struct
Chunks(Source) if (isForwardRange!Source);
Chunks!Sourcechunks(Source)(Sourcesource, size_tchunkSize)
if (isForwardRange!Source); - This range iterates over fixed-sized
chunksof sizechunkSizeof asourcerange. Source must be a forward range.chunkSizemust be greater than zero.If !isInfinite!Source andsource.walkLength is not evenly divisible bychunkSize, the back element of this range will contain fewer thanchunkSizeelements.Examples:import std.algorithm.comparison : equal; auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; auto chunks = chunks(source, 4); writeln(chunks[0]); // [1, 2, 3, 4] writeln(chunks[1]); // [5, 6, 7, 8] writeln(chunks[2]); // [9, 10] writeln(chunks.back); // chunks[2] writeln(chunks.front); // chunks[0] writeln(chunks.length); // 3 assert(equal(retro(array(chunks)), array(retro(chunks))));
- this(Source
source, size_tchunkSize); - Standard constructor
- @property auto
front();
voidpopFront();
@property boolempty();
@property typeof(this)save(); - Forward range primitives. Always present.
- @property size_t
length(); - Length. Only if hasLength!Source is
true - auto
opIndex(size_tindex);
typeof(this)opSlice(size_tlower, size_tupper); - Indexing and slicing operations. Provided only if hasSlicing!Source is
true. - @property auto
back();
voidpopBack(); - Bidirectional range primitives. Provided only if both hasSlicing!Source and hasLength!Source are
true.
- struct
EvenChunks(Source) if (isForwardRange!Source && hasLength!Source);
EvenChunks!SourceevenChunks(Source)(Sourcesource, size_tchunkCount)
if (isForwardRange!Source && hasLength!Source); - This range splits a
sourcerange intochunkCountchunks of approximately equal length. Source must be a forward range with known length.Unlike chunks,evenChunkstakes a chunk count (not size). The returned range will contain zero or moresource.length /chunkCount+ 1 elements followed bysource.length /chunkCountelements. Ifsource.length <chunkCount, some chunks will be empty.chunkCountmust not be zero, unlesssourceis also empty.Examples:import std.algorithm.comparison : equal; auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; auto chunks = evenChunks(source, 3); writeln(chunks[0]); // [1, 2, 3, 4] writeln(chunks[1]); // [5, 6, 7] writeln(chunks[2]); // [8, 9, 10]
- this(Source
source, size_tchunkCount); - Standard constructor
- @property auto
front();
voidpopFront();
@property boolempty();
@property typeof(this)save(); - Forward range primitives. Always present.
- const @property size_t
length(); - Length
- auto
opIndex(size_tindex);
typeof(this)opSlice(size_tlower, size_tupper);
@property autoback();
voidpopBack(); - Indexing, slicing and bidirectional operations and range primitives. Provided only if hasSlicing!Source is
true.
- auto
only(Values...)(auto ref Valuesvalues)
if (!is(CommonType!Values == void) || Values.length == 0); - Assemble
valuesinto a range that carries all its elements in-situ.Useful when a single value or multiple disconnectedvaluesmust be passed to an algorithm expecting a range, without having to perform dynamic memory allocation. As copying the range means copying all elements, it can be safely returned from functions. For the same reason, copying the returned range may be expensive for a large number of arguments.Parameters:Values valuesthe valuesto assemble togetherReturns:A RandomAccessRange of the assembledvalues.See Also:chain to chain rangesExamples:import std.algorithm.comparison : equal; import std.algorithm.iteration : filter, joiner, map; import std.algorithm.searching : findSplitBefore; import std.uni : isUpper; assert(equal(only('♡'), "♡")); writeln([1, 2, 3, 4].findSplitBefore(only(3))[0]); // [1, 2] assert(only("one", "two", "three").joiner(" ").equal("one two three")); string title = "The D Programming Language"; assert(title .filter!isUpper // take the upper case letters .map!only // make each letter its own range .joiner(".") // join the ranges together lazily .equal("T.D.P.L"));
- auto
enumerate(Enumerator = size_t, Range)(Rangerange, Enumeratorstart= 0)
if (isIntegral!Enumerator && isInputRange!Range); - Iterate over
rangewith an attached index variable.Each element is a std.typecons.Tuple containing the index and the element, in that order, where the index member is named index and the element member is named value. The index starts atstartand is incremented by one on every iteration.Overflow: If
Ifrangehas length, then it is an error to pass a value forstartso thatstart+range.length is bigger than Enumerator.max, thus it is ensured that overflow cannot happen.rangedoes not have length, and popFront is called when front.index == Enumerator.max, the index will overflow and continue from Enumerator.min.Parameters:Range rangethe input rangeto attach indexes toEnumerator startthe number to startthe index counter fromReturns:At minimum, an inputrange. All otherrangeprimitives are given in the resultingrangeifrangehas them. The exceptions are the bidirectional primitives, which are propagated only ifrangehas length.Example: Useful for using foreach with an index loop variable:
import std.stdio : stdin, stdout; import std.range : enumerate; foreach (lineNum, line; stdin.byLine().enumerate(1)) stdout.writefln("line #%s: %s", lineNum, line);
Examples:Canstartenumeration from a negative position:import std.array : assocArray; import std.range : enumerate; bool[int] aa = true.repeat(3).enumerate(-1).assocArray(); assert(aa[-1]); assert(aa[0]); assert(aa[1]);
- enum auto
isTwoWayCompatible(alias fn, T1, T2); - Returns
trueif fn accepts variables of type T1 and T2 in any order. The following code should compile:T1 foo(); T2 bar(); fn(foo(), bar()); fn(bar(), foo());
- enum
SearchPolicy: int; - Policy used with the searching primitives lowerBound, upperBound, and equalRange of SortedRange below.
linear- Searches in a
linearfashion. trot- Searches with a step that is grows linearly (1, 2, 3,...) leading to a quadratic search schedule (indexes tried are 0, 1, 3, 6, 10, 15, 21, 28,...) 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.
gallop- 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, 3, 7, 15, 31, 63,...) Once the search overshoots its target, the remaining interval is searched using binary search. A value is found in Ο(log(n)) time.
binarySearch- 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
binarySearchpolicy is used as the last step of trot, gallop, trotBackwards, and gallopBackwards strategies. trotBackwards- Similar to trot but starts backwards. Use it when confident that the value is around the end of the range.
gallopBackwards- Similar to gallop but starts backwards. Use it when confident that the value is around the end of the range.
- struct
SortedRange(Range, alias pred = "a < b") if (isInputRange!Range); - 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
SortedRangefrom an unsorted range r, use std.algorithm.sorting.sort which sorts r in place and returns the correspondingSortedRange. To construct aSortedRangefrom a range r that is known to be already sorted, use assumeSorted described below.Examples:import std.algorithm.sorting : sort; auto a = [ 1, 2, 3, 42, 52, 64 ]; auto r = assumeSorted(a); assert(r.contains(3)); assert(!r.contains(32)); auto r1 = sort!"a > b"(a); assert(r1.contains(3)); assert(!r1.contains(32)); writeln(r1.release()); // [64, 52, 42, 3, 2, 1]
Examples:SortedRangecould accept ranges weaker than random-access, but it is unable to provide interesting functionality for them. Therefore,SortedRangeis currently restricted to random-access ranges. No copy of the original range is ever made. If the underlying range is changed concurrently with its correspondingSortedRangein ways that break its sortedness,SortedRangewill work erratically.import std.algorithm.mutation : swap; auto a = [ 1, 2, 3, 42, 52, 64 ]; auto r = assumeSorted(a); assert(r.contains(42)); swap(a[3], a[5]); // illegal to break sortedness of original range assert(!r.contains(42)); // passes although it shouldn't
- @property bool
empty();
@property autosave();
@property ref autofront();
voidpopFront();
@property ref autoback();
voidpopBack();
ref autoopIndex(size_ti);
autoopSlice(size_ta, size_tb);
@property size_tlength();
aliasopDollar= length; - Range primitives.
- auto
release(); - Releases the controlled range and returns it.
- auto
lowerBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(Vvalue)
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,
value) istruefor all x (e.g., if pred is "less than", returns the portion of the range with elements strictly smaller thanvalue). The search schedule and its complexity are documented in SearchPolicy. See also STL's lower_bound.Examples:import std.algorithm.comparison : equal; 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 ]));
- auto
upperBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(Vvalue)
if (isTwoWayCompatible!(predFun, ElementType!Range, V)); - This function searches with policy sp to find the largest right subrange on which pred(
value, x) istruefor all x (e.g., if pred is "less than", returns the portion of the range with elements strictly greater thanvalue). The search schedule and its complexity are documented in SearchPolicy.For ranges that do not offer random access, SearchPolicy.linear is the only policy allowed (and it must be specified explicitly lest it exposes user code to unexpected inefficiencies). For random-access searches, all policies are allowed, and SearchPolicy.binarySearch is the default.See Also:STL's upper_bound.Examples:import std.algorithm.comparison : equal; auto a = assumeSorted([ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]); auto p = a.upperBound(3); assert(equal(p, [4, 4, 5, 6]));
- auto
equalRange(V)(Vvalue)
if (isTwoWayCompatible!(predFun, ElementType!Range, V) && isRandomAccessRange!Range); - Returns the subrange containing all elements e for which both pred(e,
value) and pred(value, e) evaluate tofalse(e.g., if pred is "less than", returns the portion of the range with elements equal tovalue). Uses a classic binary search with interval halving until it finds avaluethat 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 foundvalue(i.e., equal ranges are relatively small). Completes the entire search in Ο(log(n)) time. See also STL's equal_range.Examples:import std.algorithm.comparison : equal; auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; auto r = a.assumeSorted.equalRange(3); assert(equal(r, [ 3, 3, 3 ]));
- auto
trisect(V)(Vvalue)
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(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.Examples:import std.algorithm.comparison : equal; 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 ]));
- bool
contains(V)(Vvalue)
if (isRandomAccessRange!Range); - Returns
trueif and only ifvaluecan be found in range, which is assumed to be sorted. Performs Ο(log(r.length)) evaluations of pred. See also STL's binary_search. - auto
groupBy()(); - Returns a range of subranges of elements that are equivalent according to the sorting relation.
- auto
assumeSorted(alias pred = "a < b", R)(Rr)
if (isInputRange!(Unqual!R)); - Assumes
ris sorted by predicate pred and returns the corresponding SortedRange!(pred, R) havingras 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 ofrare 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.sorting.isSorted. - struct
RefRange(R) if (isInputRange!R);
autorefRange(R)(R*range)
if (isInputRange!R && !is(R == class));
autorefRange(R)(R*range)
if (isInputRange!R && is(R == class)); - Wrapper which effectively makes it possible to pass a
rangeby reference. Both the originalrangeand theRefRangewill 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 originalrangeif it were passed to it, the originalrangeis not copied but is consumed as if it were a reference type.Note: 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.Parameters:R* rangethe rangeto construct theRefRangefromReturns:ARefRange. If the given range is a class type (and thus is already a reference type), then the originalrangeis returned rather than aRefRange.Examples:Basic Exampleimport std.algorithm.searching : find; ubyte[] buffer = [1, 9, 45, 12, 22]; auto found1 = find(buffer, 45); writeln(found1); // [45, 12, 22] writeln(buffer); // [1, 9, 45, 12, 22] auto wrapped1 = refRange(&buffer); auto found2 = find(wrapped1, 45); writeln(*found2.ptr); // [45, 12, 22] writeln(buffer); // [45, 12, 22] auto found3 = find(wrapped1.save, 22); writeln(*found3.ptr); // [22] writeln(buffer); // [45, 12, 22] string str = "hello world"; auto wrappedStr = refRange(&str); writeln(str.front); // 'h' str.popFrontN(5); writeln(str); // " world" writeln(wrappedStr.front); // ' ' writeln(*wrappedStr.ptr); // " world"
Examples:opAssign Example.ubyte[] buffer1 = [1, 2, 3, 4, 5]; ubyte[] buffer2 = [6, 7, 8, 9, 10]; auto wrapped1 = refRange(&buffer1); auto wrapped2 = refRange(&buffer2); assert(wrapped1.ptr is &buffer1); assert(wrapped2.ptr is &buffer2); assert(wrapped1.ptr !is wrapped2.ptr); assert(buffer1 != buffer2); wrapped1 = wrapped2; //Everything points to the same stuff as before. assert(wrapped1.ptr is &buffer1); assert(wrapped2.ptr is &buffer2); assert(wrapped1.ptr !is wrapped2.ptr); //But buffer1 has changed due to the assignment. writeln(buffer1); // [6, 7, 8, 9, 10] writeln(buffer2); // [6, 7, 8, 9, 10] buffer2 = [11, 12, 13, 14, 15]; //Everything points to the same stuff as before. assert(wrapped1.ptr is &buffer1); assert(wrapped2.ptr is &buffer2); assert(wrapped1.ptr !is wrapped2.ptr); //But buffer2 has changed due to the assignment. writeln(buffer1); // [6, 7, 8, 9, 10] writeln(buffer2); // [11, 12, 13, 14, 15] wrapped2 = null; //The pointer changed for wrapped2 but not wrapped1. assert(wrapped1.ptr is &buffer1); assert(wrapped2.ptr is null); assert(wrapped1.ptr !is wrapped2.ptr); //buffer2 is not affected by the assignment. writeln(buffer1); // [6, 7, 8, 9, 10] writeln(buffer2); // [11, 12, 13, 14, 15]
Examples:import std.algorithm.iteration : map, joiner, group; import std.algorithm.searching : until; // fix for std.algorithm auto r = map!(x => 0)([1]); chain(r, r); zip(r, r); roundRobin(r, r); struct NRAR { typeof(r) input; @property empty() { return input.empty; } @property front() { return input.front; } void popFront() { input.popFront(); } @property save() { return NRAR(input.save); } } auto n1 = NRAR(r); cycle(n1); // non random access range version assumeSorted(r); // fix for std.range joiner([r], [9]); struct NRAR2 { NRAR input; @property empty() { return true; } @property front() { return input; } void popFront() { } @property save() { return NRAR2(input.save); } } auto n2 = NRAR2(n1); joiner(n2); group(r); until(r, 7); static void foo(R)(R r) { until!(x => x > 7)(r); } foo(r);
- pure nothrow @safe this(R*
range); - auto
opAssign(RefRangerhs); - This does not assign the pointer of
rhsto this RefRange. Rather it assigns the range pointed to byrhsto the range pointed to by this RefRange. This is because any operation on a RefRange is the same is if it occurred to the original range. The one exception is when a RefRange is assignednulleither directly or becauserhsisnull. In that case, RefRange no longer refers to the original range but isnull. - void
opAssign(typeof(null)rhs); - inout pure nothrow @property @safe inout(R*)
ptr(); - A pointer to the wrapped range.
- @property auto
front();
const @property autofront();
@property autofront(ElementType!Rvalue); - @property bool
empty();
const @property boolempty(); - void
popFront(); - @property auto
save();
const @property autosave();
autoopSlice();
const autoopSlice(); - Only defined if isForwardRange!R is
true. - @property auto
back();
const @property autoback();
@property autoback(ElementType!Rvalue);
voidpopBack(); - Only defined if isBidirectionalRange!R is
true. - ref auto
opIndex(IndexType)(IndexTypeindex);
const ref autoopIndex(IndexType)(IndexTypeindex); - Only defined if isRandomAccesRange!R is
true. - auto
moveFront(); - Only defined if hasMobileElements!R and isForwardRange!R are
true. - auto
moveBack(); - Only defined if hasMobileElements!R and isBidirectionalRange!R are
true. - auto
moveAt(size_tindex); - Only defined if hasMobileElements!R and isRandomAccessRange!R are
true. - @property auto
length();
const @property autolength();
aliasopDollar= length; - Only defined if hasLength!R is
true. - auto
opSlice(IndexType1, IndexType2)(IndexType1begin, IndexType2end);
const autoopSlice(IndexType1, IndexType2)(IndexType1begin, IndexType2end); - Only defined if hasSlicing!R is
true.
- auto
bitwise(R)(auto ref Rrange)
if (isInputRange!R && isIntegral!(ElementType!R)); - Bitwise adapter over an integral type
range. Consumes therangeelements bit by bit, from the least significant bit to the most significant bit.Parameters:R an integral input rangeto iterate overR rangerangeto consume bit by byReturns:A Bitwise inputrangewith propagated forward, bidirectional and random access capabilitiesExamples:import std.algorithm.comparison : equal; import std.format : format; // 00000011 00001001 ubyte[] arr = [3, 9]; auto r = arr.bitwise; // iterate through it as with any other range writeln(format("%(%d%)", r)); // "1100000010010000" assert(format("%(%d%)", r.retro).equal("1100000010010000".retro)); auto r2 = r[5 .. $]; // set a bit r[2] = 1; writeln(arr[0]); // 7 writeln(r[5]); // r2[0]
Examples:You can usebitwiseto implement an uniform bool generatorimport std.random : rndGen; import std.algorithm.comparison : equal; auto rb = rndGen.bitwise; static assert(isInfinite!(typeof(rb))); auto rb2 = rndGen.bitwise; // Don't forget that structs are passed by value assert(rb.take(10).equal(rb2.take(10)));
- struct
NullSink; - An OutputRange that discards the data it receives.Examples:
import std.algorithm.iteration : map; import std.algorithm.mutation : copy; [4, 5, 6].map!(x => x * 2).copy(NullSink()); // data is discarded
- auto
tee(Flag!"pipeOnPop" pipeOnPop = Yes.pipeOnPop, R1, R2)(R1inputRange, R2outputRange)
if (isInputRange!R1 && isOutputRange!(R2, ElementType!R1));
autotee(alias fun, Flag!"pipeOnPop" pipeOnPop = Yes.pipeOnPop, R1)(R1inputRange)
if (is(typeof(fun) == void) || isSomeFunction!fun); - 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 intermediate values in a long chain of range code, performing some operation with side-effects on each call to front or popFront, or diverting the elements of a range into an auxiliary OutputRange.It is important to note that as the resultant range is evaluated lazily, in the case of the version ofteethat takes a function, the function will not actually be executed until the range is "walked" using functions that evaluate ranges, such as std.array.array or std.algorithm.iteration.fold.Parameters:pipeOnPop If Yes.pipeOnPop, simply iterating the range without ever calling front is enough to have teemirror elements tooutputRange(or, respectively, fun). If No.pipeOnPop, only elements for which front does get called will be also sent tooutputRange/fun.R1 inputRangeThe input range beeing passed through. R2 outputRangeThis range will receive elements of inputRangeprogressively as iteration proceeds.fun This function will be called with elements of inputRangeprogressively as iteration proceeds.Returns:An input range that offers the elements ofinputRange. Regardless of whetherinputRangeis a more powerful range (forward, bidirectional etc), the result is always an input range. Reading this causesinputRangeto be iterated and returns its elements in turn. In addition, the same elements will be passed tooutputRangeor fun as well.See Also:Examples:import std.algorithm.comparison : equal; import std.algorithm.iteration : filter, map; // Sum values while copying int[] values = [1, 4, 9, 16, 25]; int sum = 0; auto newValues = values.tee!(a => sum += a).array; assert(equal(newValues, values)); writeln(sum); // 1 + 4 + 9 + 16 + 25 // Count values that pass the first filter int count = 0; auto newValues4 = values.filter!(a => a < 10) .tee!(a => count++) .map!(a => a + 1) .filter!(a => a < 10); //Fine, equal also evaluates any lazy ranges passed to it. //count is not 3 until equal evaluates newValues4 assert(equal(newValues4, [2, 5])); writeln(count); // 3
- auto
padLeft(R, E)(Rr, Ee, size_tn)
if ((isInputRange!R && hasLength!R || isForwardRange!R) && !is(CommonType!(ElementType!R, E) == void)); - Extends the length of the input range
rby padding out the start of the range with the elemente. The elementemust be of a common type with the element type of the rangeras defined by std.traits.CommonType. Ifnis less than the length of ofr, thenris returned unmodified.Ifris a string with Unicode characters in it,padLeftfollows D's rules about length for strings, which is not the number of characters, or graphemes, but instead the number of encoding units. If you want to treat each grapheme as only one encoding unit long, then call std.uni.byGrapheme before calling this function. Ifrhas a length, then this is Ο(1). Otherwise, it's Ο(r.length).Parameters:R ran input range with a length, or a forward range E eelement to pad the range with size_t nthe length to pad to Returns:A range containing the elements of the original range with the extra padding See Also: std.string.leftJustifierExamples:import std.algorithm.comparison : equal; assert([1, 2, 3, 4].padLeft(0, 6).equal([0, 0, 1, 2, 3, 4])); assert([1, 2, 3, 4].padLeft(0, 3).equal([1, 2, 3, 4])); assert("abc".padLeft('_', 6).equal("___abc"));
- auto
padRight(R, E)(Rr, Ee, size_tn)
if (isInputRange!R && !isInfinite!R && !is(CommonType!(ElementType!R, E) == void)); - Extend the length of the input range
rby padding out the end of the range with the elemente. The elementemust be of a common type with the element type of the rangeras defined by std.traits.CommonType. Ifnis less than the length of ofr, then the contents ofrare returned.The range primitives that the resulting range provides depends whether or notrprovides them. Except the functions back and popBack, which also require the range to have a length as well as back and popBackParameters:R ran input range with a length E eelement to pad the range with size_t nthe length to pad to Returns:A range containing the elements of the original range with the extra padding See Also: std.string.rightJustifierExamples:import std.algorithm.comparison : equal; assert([1, 2, 3, 4].padRight(0, 6).equal([1, 2, 3, 4, 0, 0])); assert([1, 2, 3, 4].padRight(0, 4).equal([1, 2, 3, 4])); assert("abc".padRight('_', 6).equal("abc___"));