This module defines the notion of a range. Ranges generalize the concept ofarrays, lists, or anything that involves sequential access. This abstractionenables the same set of algorithms (see
std.algorithm) to be usedwith a vast variety of different concrete types. For example,a linear search algorithm such as
std.algorithm.searching.findworks not just for arrays, but for linked-lists, input files,incoming network data, etc.
SubmodulesThis module has two submodules:
The
std.range.primitives submoduleprovides basic range functionality. It defines several templates for testingwhether a given object is a range, what kind of range it is, and providessome common range operations.
The
std.range.interfaces submoduleprovides object-based interfaces for working with ranges via runtimepolymorphism.
The remainder of this module provides a rich set of range creation andcomposition 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 firstn elements from the given range. |
| dropBack | Creates the range that results from discarding the lastn elements from the given range. |
| dropExactly | Creates the range that results from discarding exactlyn of the first elements from the given range. |
| dropBackExactly | Creates the range that results from discarding exactlyn of the last elements from the given range. |
| dropOne | Creates the range that results from discarding the first element from the given range. |
| dropBackOne | Creates the range that results from discarding the last element 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. |
| generate | Creates a range by successive calls to a given function. This allows to create ranges as a single delegate. |
| 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 | Iteratesn ranges in lockstep, for use in aforeach loop. Similar tozip, except thatlockstep is designed especially forforeach 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. |
| refRange | 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. |
| repeat | Creates a range that consists of a single element repeatedn times, or an infinite range repeating that element indefinitely. |
| retro | Iterates a bidirectional range backwards. |
| roundRobin | Givenn ranges, creates a new range that return then first elements of each range, in turn, then the second element of each range, and so on, in a round-robin fashion. |
| sequence | Similar torecurrence, except that a random-access range is created. |
| slide | Creates a range that returns a fixed-size sliding window over the original range. Unlike chunks, it advances a configurable number of items at a time, not one chunk at a time. |
| stride | Iterates a range with striden. |
| tail | Return a range advanced to withinn elements of the end of the given range. |
| take | Creates a sub-range consisting of only up to the firstn elements of the given range. |
| takeExactly | Liketake, but assumes the given range actually hasn elements, and therefore also defines thelength 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 then'th elements of the given random-access ranges. |
| zip | Givenn ranges, creates a range that successively returns a tuple of all the first elements, a tuple of all the second elements, etc. |
auto
retro(Range)(Range
r)
if (isBidirectionalRange!(Unqual!Range));
Iterates a bidirectional range backwards. The original range can beaccessed by using thesource property. Applying retro twice tothe same range yields the original range.
Parameters:Ranger | the bidirectional range to iterate backwards |
Returns:A bidirectional range with length ifr also provides a length. Or, ifr is a random access range, then the return value will be random access as well.
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[]).sourceis a[]);assert(retro(retro(a[]))is a[]);
auto
stride(Range)(Range
r, size_t
n)
if (isInputRange!(Unqual!Range));
Iterates ranger with striden. If the range is arandom-access range, moves by indexing into the range; otherwise,moves by successive calls topopFront. Applying stride twice tothe same range results in a stride with a step that is theproduct of the two applications. It is an error forn to be 0.
Parameters:Ranger | theinput range to stride over |
size_tn | the 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
std.range.primitives.hasLength 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...)(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 functionchain takes anynumber of ranges and returns aChain!(R1, R2,...) object. Theranges may be different, but they must have the same element type. Theresult is a range that offers thefront,popFront, andempty primitives. If all input ranges offer random access andlength,Chain offers them as well.
Note that repeated random access of the resulting range is likelyto perform somewhat badly since lengths of the ranges in the chain have to beadded up for each random access operation. Random access to elements ofthe first remaining range is still efficient.
If only one range is offered to
Chain or
chain, the
Chain type exits the picture by aliasing itself directly to thatrange's type.
Returns:An input range at minimum. If all of the ranges inrs provide a range primitive, the returned range will also provide that range primitive.
See Also:only to chain values to a range
Examples: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);// 7writeln(s[5]);// 6assert(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 them
import std.algorithm.comparison : equal;import std.algorithm.sorting : sort;int[] arr1 = [5, 2, 8];int[] arr2 = [3, 7, 9];int[] arr3 = [1, 4, 6];// in-place sorting across all of the arraysauto 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]));
Examples:Due to safe type promotion in D, chaining together differentcharacter ranges results in a
uint range.
Use
byChar,
byWchar,and
byDchar on the rangesto get the type you need.
import std.utf : byChar, byCodeUnit;auto s1 ="string one";auto s2 ="string two";// s1 and s2 front is dchar because of auto-decodingstaticassert(is(typeof(s1.front) ==dchar) &&is(typeof(s2.front) ==dchar));auto r1 = s1.chain(s2);// chains of ranges of the same character type give that same typestaticassert(is(typeof(r1.front) ==dchar));auto s3 ="string three".byCodeUnit;staticassert(is(typeof(s3.front) ==immutablechar));auto r2 = s1.chain(s3);// chaining ranges of mixed character types gives `dchar`staticassert(is(typeof(r2.front) ==dchar));// use byChar on character ranges to correctly convert them to UTF-8auto r3 = s1.byChar.chain(s3);staticassert(is(typeof(r3.front) ==immutablechar));
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.
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 rangethat offers the weakest capabilities of the two (e.g.ForwardRange ifR1 is a random-access range andR2 is a forward range).
Parameters:boolcondition | which range to choose:r1 iftrue,r2 otherwise |
R1r1 | the "true" range |
R2r2 | the "false" range |
Returns:A range type dependent onR1 andR2.
Examples:import std.algorithm.comparison : equal;import std.algorithm.iteration : filter, map;auto data1 = only(1, 2, 3, 4).filter!(a => a != 3);auto data2 = only(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.staticassert(!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.returnchoose(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(only(1, 2, 4)));result = chooseRange(false);assert(result.equal(only(6, 7, 8, 9))); 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.
The ranges may be different, but they must have compatible element types. Theresult is a range that offers the weakest capabilities of allRanges.
Parameters:size_tindex | which range to choose, must be less than the number of ranges |
Rangesrs | two or more ranges |
Returns:The indexed range. If rs consists of only one range, the return type is an alias of that range's type.
Examples:auto test(){import std.algorithm.comparison : equal;int[4] sarr1 = [1, 2, 3, 4];int[2] sarr2 = [5, 6];int[1] sarr3 = [7];auto arr1 = sarr1[];auto arr2 = sarr2[];auto arr3 = sarr3[]; {auto s =chooseAmong(0, arr1, arr2, arr3);auto t = s.save; writeln(s.length);// 4 writeln(s[2]);// 3 s.popFront();assert(equal(t, only(1, 2, 3, 4))); } {auto s =chooseAmong(1, arr1, arr2, arr3); writeln(s.length);// 2 s.front = 8;assert(equal(s, only(8, 6))); } {auto s =chooseAmong(1, arr1, arr2, arr3); writeln(s.length);// 2 s[1] = 9;assert(equal(s, only(8, 9))); } {auto s =chooseAmong(1, arr2, arr1, arr3)[1 .. 3]; writeln(s.length);// 2assert(equal(s, only(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, only(1, 2, 5))); s.back = 3;assert(equal(s, only(1, 2, 3))); } {uint[5] foo = [1, 2, 3, 4, 5];uint[5] 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(0, cycle(arr2), cycle(arr3));assert(isInfinite!(typeof(s)));assert(!s.empty); writeln(s[100]);// 8 writeln(s[101]);// 9assert(s[0 .. 3].equal(only(8, 9, 8))); }return 0;}// works at runtimeauto a = test();// and at compile timestatic b = test(); auto
roundRobin(Rs...)(Rs
rs)
if (Rs.length > 1 && allSatisfy!(isInputRange, staticMap!(Unqual, Rs)));
roundRobin(r1, r2, r3) yieldsr1.front, thenr2.front,thenr3.front, after which it pops off one element from each andcontinues again fromr1. 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 thatfinish 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:roundRobin can 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){staticif (hasLength!R)immutable len = range.length;elseimmutable len = range.save.walkLength;returnroundRobin( range, element.repeat(len - 1) );}assert(interleave([1, 2, 3], 0).equal([1, 0, 2, 0, 3])); auto
radial(Range, I)(Range
r, I
startingIndex)
if (isRandomAccessRange!(Unqual!Range) && hasLength!(Unqual!Range) && hasSlicing!(Unqual!Range) && isIntegral!I);
auto
radial(R)(R
r)
if (isRandomAccessRange!(Unqual!R) && hasLength!(Unqual!R) && hasSlicing!(Unqual!R));
Iterates a random-access range starting from a given point andprogressively extending left and right from that point. If no initialpoint is given, iteration starts from the middle of therange. Iteration spans the entire range.
WhenstartingIndex is 0 the range will be fully iterated in orderand in reverse order whenr.length is given.
Parameters:Ranger | a random access range with length and slicing |
IstartingIndex | the index to begin iteration from |
Returns:A forward range with length
Examples: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)(R
input, size_t
n)
if (isInputRange!(Unqual!R));
struct
Take(Range) if (isInputRange!(Unqual!Range) && !(!isInfinite!(Unqual!Range) && hasSlicing!(Unqual!Range) || is(Range T ==
Take!T)));
template
Take(R) if (isInputRange!(Unqual!R) && (!isInfinite!(Unqual!R) && hasSlicing!(Unqual!R) || is(R T ==
Take!T)))
Lazily takes only up ton elements of a range. This isparticularly useful when using with infinite ranges.
Unlike
takeExactly,
take does not require that thereare
n or more elements in
input. As a consequence, lengthinformation is not applied to the result unless
input also haslength information.
Parameters:Rinput | aninput range to iterate over up ton times |
size_tn | the number of elements to take |
Returns:At minimum, an input range. If the range offers random access andlength,take offers them as well.
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);// 5writeln(s[4]);// 5assert(equal(s, [ 1, 2, 3, 4, 5 ][]));
Examples:If the range runs out before
n elements,
take simply 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);// 3assert(equal(t, [ 1, 2, 3 ]));
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, n)always defines the
length property (and initializes it to
n)even when
range itself does not define
length.
The result of
takeExactly is identical to that of
take incases where the original range defines
length or is infinite.
Unlike
take, however, it is illegal to pass a range with less than
n elements to
takeExactly; 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]));staticassert(is(typeof(b.length) == size_t));writeln(b.length);// 3writeln(b.front);// 1writeln(b.back);// 3
auto
takeOne(R)(R
source)
if (isInputRange!R);
Returns a range with at most one element; for example,takeOne([42, 43, 44]) returns a range consisting of the integer42. CallingpopFront() off that range renders it empty.
In effect
takeOne(r) is somewhat equivalent to
take(r, 1) but incertain interfaces it is important to know statically that the range may onlyhave at most one element.
The type returned by
takeOne is a random-access range with lengthregardless of
R's capabilities, as long as it is a forward range.(another feature that distinguishes
takeOne from
take). If(D R) is an input range but not a forward range, return type is an inputrange with all random-access capabilities except save.
Examples:auto s =takeOne([42, 43, 44]);staticassert(isRandomAccessRange!(typeof(s)));writeln(s.length);// 1assert(!s.empty);writeln(s.front);// 42s.front = 43;writeln(s.front);// 43writeln(s.back);// 43writeln(s[0]);// 43s.popFront();writeln(s.length);// 0assert(s.empty);
auto
takeNone(R)()
if (isInputRange!R);
Returns an empty range which is statically known to be empty and is guaranteed to havelength and be random access regardless ofR's capabilities.
Examples:auto range =takeNone!(int[])();writeln(range.length);// 0assert(range.empty);
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 returntakeExactly(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)(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 ofrange.
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:Rangerange | range to get tail of |
size_tn | maximum number of elements to include in tail |
Returns:Returns the tail ofrange augmented with length information
Examples:// tail -c nwriteln([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=nimport std.algorithm.comparison : equal;import std.algorithm.iteration : joiner;import std.exception : assumeWontThrow;import std.string : lineSplitter;assert("one\ntwo\nthree" .lineSplitter .tail(2) .joiner("\n") .equal("two\nthree") .assumeWontThrow); R
drop(R)(R
range, size_t
n)
if (isInputRange!R);
R
dropBack(R)(R
range, size_t
n)
if (isBidirectionalRange!R);
Convenience function which calls
std.range.primitives.popFrontN(range,n) and returns
range.
drop makes it easier to pop elements from a range and then pass it to another function within a single expression, whereas
popFrontN would require multiple statements.
Notedrop anddropBack will only popup ton elements but will stop if the range is empty first. In other languages this is sometimes calledskip.
Parameters:Rrange | theinput range to drop from |
size_tn | the number of elements to drop |
Returns:range with up ton elements dropped
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 ")); Examples:import std.algorithm.comparison : equal;writeln([0, 2, 1, 5, 0, 3].dropBack(3));// [0, 2, 1]writeln("hello world".dropBack(6));// "hello"assert("hello world".dropBack(50).empty);assert("hello world".drop(4).dropBack(4).equal("o w")); R
dropExactly(R)(R
range, size_t
n)
if (isInputRange!R);
R
dropBackExactly(R)(R
range, size_t
n)
if (isBidirectionalRange!R);
Similar to
drop and
dropBack but they call
range.popFrontExactly(n) and
range.popBackExactly(n) instead.
NoteUnlikedrop,dropExactly will assume that the range holds at leastn elements. This makesdropExactly faster thandrop, but it also means that ifrange does not contain at leastn elements, it will attempt to callpopFront on an empty range, which is undefined behavior. So, only usepopFrontExactly when it is guaranteed thatrange holds at leastn elements.
Parameters:Rrange | theinput range to drop from |
size_tn | the number of elements to drop |
Returns:range withn elements dropped
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)(R
range)
if (isInputRange!R);
R
dropBackOne(R)(R
range)
if (isBidirectionalRange!R);
Convenience function which callsrange.popFront() and returnsrange.dropOne makes it easier to pop an element from a range and then pass it to another function within a single expression, whereaspopFront would require multiple statements.
dropBackOne provides 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;assert(assumeWontThrow(s.dropOne() =="本語"));assert(assumeWontThrow(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!T
repeat(T)(T
value);
Take!(Repeat!T)
repeat(T)(T
value, size_t
n);
Create a range which repeats one value.
Parameters:Tvalue | the value to repeat |
size_tn | the number of times to repeatvalue |
Returns:If
n is not defined, an infinite random access range with slicing.
If
n is defined, a random access range with slicing.
Examples:import std.algorithm.comparison : equal;assert(5.repeat().take(4).equal([5, 5, 5, 5]));
Examples:import std.algorithm.comparison : equal;assert(5.repeat(4).equal([5, 5, 5, 5]));
@property inout(T)
front() inout;
@property inout(T)
back() inout;
enum bool
empty;
void
popFront();
void
popBack();
@property auto
save() inout;
inout(T)
opIndex(size_t) inout;
auto
opSlice(size_t
i, size_t
j);
enum auto
opDollar;
auto
opSlice(size_t, DollarToken) inout;
Range primitives
auto
generate(Fun)(Fun
fun)
if (isCallable!fun);
auto
generate(alias fun)()
if (isCallable!fun);
Given callable (
std.traits.isCallable)
fun, create as a rangewhose front is defined by successive calls to
fun().This is especially useful to call function with global side effects (randomfunctions), or to create ranges expressed as a single delegate, rather thanan entire
front/
popFront/
empty structure.
fun maybe be passed either a template alias parameter (existingfunction, delegate, struct type defining
static opCall) ora run-time value argument (delegate, function object).The result range models an InputRange(
std.range.primitives.isInputRange).The resulting range will call
fun() on construction, and every call to
popFront, and the cached value will be returned when
front is called.
Returns:aninputRange where each element represents another call to fun.
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 delegateauto 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);
template
Cycle(R) if (isInfinite!R)
struct
Cycle(R) if (isStaticArray!R);
auto
cycle(R)(R
input)
if (isInputRange!R);
Cycle!R
cycle(R)(R
input, size_t
index = 0)
if (isRandomAccessRange!R && !isInfinite!R);
@system Cycle!R
cycle(R)(ref R
input, size_t
index = 0)
if (isStaticArray!R);
Repeats the given forward range ad infinitum. If the original range isinfinite (fact that would makeCycle the identity application),Cycle detects that and aliases itself to the range typeitself. That works for non-forward ranges too.If the original range has random access,Cycle offersrandom access and also offers a constructor taking an initial positionindex.Cycle works with static arrays in addition to ranges,mostly for performance reasons.
NoteThe input range must not be empty.
TipThis is a great way to implement simple circular buffers.
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 ]));
this(R
input, size_t
index = 0);
@property ref auto
front();
@property ref auto
front() const;
@property void
front(ElementType!R
val);
enum bool
empty;
void
popFront();
ref auto
opIndex(size_t
n);
ref auto
opIndex(size_t
n) const;
void
opIndexAssign(ElementType!R
val, size_t
n);
@property Cycle
save();
enum auto
opDollar;
auto
opSlice(size_t
i, size_t
j);
auto
opSlice(size_t
i, DollarToken);
Range primitives
struct
Zip(Ranges...) if (Ranges.length && allSatisfy!(isInputRange, Ranges));
auto
zip(Ranges...)(Ranges
ranges)
if (Ranges.length && allSatisfy!(isInputRange, Ranges));
auto
zip(Ranges...)(StoppingPolicy
sp, Ranges
ranges)
if (Ranges.length && allSatisfy!(isInputRange, Ranges));
Iterate several ranges in lockstep. The element type is a proxy tuple that allows accessing the current element in thenth range by usinge[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.
Parameters:StoppingPolicysp | controls whatzip will do if the ranges are different lengths |
Rangesranges | the ranges to zip together |
Returns:At minimum, an input range.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.
Throws:AnException if all of the ranges are not the same length andsp is set toStoppingPolicy.requireSameLength.
LimitationsThe@nogc andnothrow attributes cannot be inferred for theZip struct becauseStoppingPolicy can vary at runtime. This limitation is not shared by the anonymous range returned by thezip function when not given an explicitStoppingPolicy as an argument.
Examples:import std.algorithm.comparison : equal;import std.algorithm.iteration : map;// pairwise sumauto arr = only(0, 1, 2);auto part1 =zip(arr, arr.dropOne).map!"a[0] + a[1]";assert(part1.equal(only(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 foreachforeach (e1, e2;zip(a, b)){ writeln(e1);// a[idx] writeln(e2);// b[idx] ++idx;} Examples:zip is 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 sortingwriteln(b);// ["b", "c", "a"]
this(R
rs, StoppingPolicy
s = StoppingPolicy.shortest);
Builds an object. Usually this is invoked indirectly by using the
zip function.
Returnstrue if the range is at end. The test depends on the stopping policy.
@property ElementType
front();
Returns the current iterated element.
@property void
front(ElementType
v);
Sets the front of all iterated ranges.
Moves out the front.
@property ElementType
back();
Returns the rightmost element.
Moves out the back.
Returns the rightmost element.
@property void
back(ElementType
v);
Returns the current iterated element.
Returns the rightmost element.
Advances to the next element in all controlled ranges.
CallspopBack for all controlled ranges.
Returns the length of this range. Defined only if all ranges definelength.
Returns the length of this range. Defined only if all ranges definelength.
auto
opSlice(size_t
from, size_t
to);
Returns a slice of the range. Defined only if all range define slicing.
ElementType
opIndex(size_t
n);
Returns thenth element in the composite range. Defined if all ranges offer random access.
void
opIndexAssign(ElementType
v, size_t
n);
Assigns to thenth 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_t
n);
Destructively reads thenth 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.
Dictates how iteration in a
zip and
lockstep should stop. By default stop at the end of the shortest of all ranges.
Examples:import std.algorithm.comparison : equal;import std.exception : assertThrown;import std.range.primitives;import std.typecons : tuple;auto a = [1, 2, 3];auto b = [4, 5, 6, 7];auto shortest = zip(StoppingPolicy.shortest, a, b);assert(shortest.equal([ tuple(1, 4), tuple(2, 5), tuple(3, 6)]));auto longest = zip(StoppingPolicy.longest, a, b);assert(longest.equal([ tuple(1, 4), tuple(2, 5), tuple(3, 6), tuple(0, 7)]));auto same = zip(StoppingPolicy.requireSameLength, a, b);same.popFrontN(3);assertThrown!Exception(same.popFront);
Stop when the shortest range is exhausted
Stop when the longest range is exhausted
Require that all ranges are equal
struct
Lockstep(Ranges...) if (Ranges.length > 1 && allSatisfy!(isInputRange, Ranges));
Lockstep!Ranges
lockstep(Ranges...)(Ranges
ranges)
if (allSatisfy!(isInputRange, Ranges));
Lockstep!Ranges
lockstep(Ranges...)(Ranges
ranges, StoppingPolicy
s)
if (allSatisfy!(isInputRange, Ranges));
Iterate multiple ranges in lockstep using a
foreach loop. In contrast to
zip it allows reference access to its elements. 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.
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.
See Also:ziplockstep is similar to
zip, but
zip bundles its elements and returns a range.
lockstep also supports reference access. Use
zip if you want to pass the result to a range function.
Examples:int[6] arr1 = [1,2,3,4,5,100];int[5] arr2 = [6,7,8,9,10];foreach (ref a, b;lockstep(arr1[], arr2[])){ a += b;}writeln(arr1);// [7, 9, 11, 13, 15, 100] Examples:Lockstep also supports iterating with an index variable:
int[3] arr1 = [1,2,3];int[3] arr2 = [4,5,6];foreach (index, a, b;lockstep(arr1[], arr2[])){ writeln(arr1[index]);// a writeln(arr2[index]);// b} this(Ranges
ranges, StoppingPolicy
sp = StoppingPolicy.shortest);
struct
Recurrence(alias fun, StateType, size_t stateSize);
Recurrence!(fun, CommonType!State, State.length)
recurrence(alias fun, State...)(State
initial);
Creates a mathematical sequence given the initial values and arecurrence function that computes the next value from the existingvalues. The sequence comes in the form of an infinite forwardrange. The typeRecurrence itself is seldom used directly; mostoften, recurrences are obtained by calling the functionrecurrence.
When calling
recurrence, the function that computes the nextvalue is specified as a template argument, and the initial values inthe recurrence are passed as regular arguments. For example, in aFibonacci sequence, there are two initial values (and therefore astate size of 2) because computing the next Fibonacci value needs thepast 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 anopaque 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". Thegiven string must return the desired value for
a[n] given
a[n - 1],
a[n - 2],
a[n - 3],...,
a[n - stateSize]. Thestate size is dictated by the number of arguments passed to the callto
recurrence. The
Recurrence struct itself takes care ofmanaging the recurrence'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);
auto
sequence(alias fun, State...)(State
args);
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 aTuple 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);// 1odds.popFront();writeln(odds.front);// 3odds.popFront();writeln(odds.front);// 5 Examples:Triangular numbers, using function in lambda form:
auto tri =sequence!((a,n) => n*(n+1)/2)();// Note random accesswriteln(tri[0]);// 0writeln(tri[3]);// 6writeln(tri[1]);// 1writeln(tri[4]);// 10writeln(tri[2]);// 3
Examples:Fibonacci numbers, using function in explicit form:
import std.math.exponential : pow;import std.math.rounding : round;import std.math.algebraic : sqrt;staticulong computeFib(S)(S state, size_t n){// Binet's formulareturncast(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 [] operatorwriteln(fib[1]);// 1writeln(fib[4]);// 5writeln(fib[3]);// 3writeln(fib[2]);// 2writeln(fib[9]);// 55 auto
iota(B, E, S)(B
begin, E
end, S
step)
if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) && isIntegral!S);
auto
iota(B, E)(B
begin, E
end)
if (isFloatingPoint!(CommonType!(B, E)));
auto
iota(B, E)(B
begin, E
end)
if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E)));
auto
iota(E)(E
end)
if (is(typeof(iota(E(0),end))));
auto
iota(B, E, S)(B
begin, E
end, S
step)
if (isFloatingPoint!(CommonType!(B, E, S)));
auto
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))));
Creates a range of values that span the given starting and stopping values.
Parameters:Bbegin | The starting value. |
Eend | The value that serves as the stopping criterion. This value is not included in the range. |
Sstep | The value to add to the current value at each iteration. |
Returns:A range that goes through the numbers
begin,
begin + step,
begin + 2 * step,
..., up to and excluding
end.
The two-argument overloads have
step = 1. If
begin < end && step < 0 or
begin > end && step > 0 or
begin == end, then an empty range is returned. If
step == 0 then
begin == end is 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.
An integral iota also supports
in operator from the right. It takes the stepping into account, the integral won't be considered contained if it falls between two consecutive values of the range.
contains does the same as in, but from lefthand side.
Example
void main(){import std.stdio;// The following groups all produce the same output of:// 0 1 2 3 4foreach (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.operations : isClose;auto r =iota(0, 10, 1);assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));assert(3in r);assert(r.contains(3));//Same as aboveassert(!(10in r));assert(!(-8in r));r =iota(0, 11, 3);assert(equal(r, [0, 3, 6, 9]));writeln(r[2]);// 6assert(!(2in r));auto rf =iota(0.0, 0.5, 0.1);assert(isClose(rf, [0.0, 0.1, 0.2, 0.3, 0.4]));
enum
TransverseOptions: int;
Examples:import std.algorithm.comparison : equal;import std.exception : assertThrown;auto arr = [[1, 2], [3, 4, 5]];auto r1 = arr.frontTransversal!(TransverseOptions.assumeJagged);assert(r1.equal([1, 3]));// throws on constructionassertThrown!Exception(arr.frontTransversal!(TransverseOptions.enforceNotJagged));auto r2 = arr.frontTransversal!(TransverseOptions.assumeNotJagged);assert(r2.equal([1, 3]));// either assuming or checking for equal lengths makes// the result a random access rangewriteln(r2[0]);// 1staticassert(!__traits(compiles, r1[0]));
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.
struct
FrontTransversal(Ror, TransverseOptions opt = TransverseOptions.assumeJagged);
FrontTransversal!(RangeOfRanges, opt)
frontTransversal(TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges)(RangeOfRanges
rr);
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 =newint[][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 auto
front();
ElementType
moveFront();
void
popFront();
Forward range primitives.
@property FrontTransversal
save();
Duplicates thisfrontTransversal. Note that only the encapsulating range of range will be duplicated. Underlying ranges will not be duplicated.
@property ref auto
back();
void
popBack();
ElementType
moveBack();
Bidirectional primitives. They are offered if isBidirectionalRange!RangeOfRanges.
ref auto
opIndex(size_t
n);
ElementType
moveAt(size_t
n);
void
opIndexAssign(ElementType
val, size_t
n);
Random-access primitive. It is offered if isRandomAccessRange!RangeOfRanges && (opt == TransverseOptions.assumeNotJagged || opt == TransverseOptions.enforceNotJagged).
typeof(this)
opSlice(size_t
lower, size_t
upper);
Slicing if offered ifRangeOfRanges 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)(RangeOfRanges
rr, size_t
n);
Given a range of ranges, iterate transversally through thenth element of each of the enclosed ranges. This function is similar tounzip in other languages.
Parameters:| opt | Controls the assumptions the function makes about the lengths of the ranges |
RangeOfRangesrr | An 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 ofrr provides them.
Examples:import std.algorithm.comparison : equal;int[][] x =newint[][2];x[0] = [1, 2];x[1] = [3, 4];auto ror =transversal(x, 1);assert(equal(ror, [ 2, 4 ]));
Examples:The following code does a full unzip
import std.algorithm.comparison : equal;import std.algorithm.iteration : map;int[][] y = [[1, 2, 3], [4, 5, 6]];auto z = y.front.walkLength.iota.map!(i =>transversal(y, i));assert(equal!equal(z, [[1, 4], [2, 5], [3, 6]]));
this(RangeOfRanges
input, size_t
n);
Construction from an input and an index.
enum bool
empty;
@property ref auto
front();
E
moveFront();
@property void
front(E
val);
void
popFront();
@property typeof(this)
save();
Forward range primitives.
@property ref auto
back();
void
popBack();
E
moveBack();
@property void
back(E
val);
Bidirectional primitives. They are offered if isBidirectionalRange!RangeOfRanges.
ref auto
opIndex(size_t
n);
E
moveAt(size_t
n);
void
opIndexAssign(E
val, size_t
n);
Random-access primitive. It is offered if isRandomAccessRange!RangeOfRanges && (opt == TransverseOptions.assumeNotJagged || opt == TransverseOptions.enforceNotJagged).
typeof(this)
opSlice(size_t
lower, size_t
upper);
Slicing if offered ifRangeOfRanges supports slicing and all the conditions for supporting indexing are met.
Transposed!(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 thei'th subrangecontains thei'th elements of the original subranges.
Parameters:| opt | Controls the assumptions the function makes about the lengths of the ranges (i.e. jagged or not) |
RangeOfRangesrr | Range of ranges |
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 =newint[][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)(Source
source, Indices
indices);
This struct takes two ranges,source andindices, and creates a viewofsource as if its elements were reordered according toindices.indices may include only a subset of the elements ofsource andmay also repeat elements.
Source must be a random access range. The returned range will bebidirectional or random-access ifIndices is bidirectional orrandom-access, respectively.
Examples:import std.algorithm.comparison : equal;autosource = [1, 2, 3, 4, 5];autoindices = [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();
void
popFront();
@property 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();
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
@property Source
source();
Returns the source range.
@property Indices
indices();
Returns the indices range.
size_t
physicalIndex(size_t
logicalIndex);
Returns the physical index into the source range corresponding to a given logical index. This is useful, for example, when indexing anIndexed 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 (isInputRange!Source);
Chunks!Source
chunks(Source)(Source
source, size_t
chunkSize)
if (isInputRange!Source);
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 evenlydivisible by
chunkSize, the back element of this range will containfewer than
chunkSize elements.
If
Source is a forward range, the resulting range will be forward ranges aswell. Otherwise, the resulting chunks will be input ranges consuming the sameinput: iterating over
front will shrink the chunk such that subsequentinvocations of
front will no longer return the full chunk, and calling
popFront on the outer range will invalidate any lingering references toprevious values of
front.
Parameters:Sourcesource | Range from which the chunks will be selected |
size_tchunkSize | Chunk size |
Examples:import std.algorithm.comparison : equal;autosource = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];autochunks =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);// 3assert(equal(retro(array(chunks)), array(retro(chunks))));
Examples:Non-forward input ranges are supported, but with limited semantics.
import std.algorithm.comparison : equal;int i;// The generator doesn't save state, so it cannot be a forward range.auto inputRange = generate!(() => ++i).take(10);// We can still process it in chunks, but it will be single-pass only.auto chunked = inputRange.chunks(2);assert(chunked.front.equal([1, 2]));assert(chunked.front.empty);// Iterating the chunk has consumed itchunked.popFront;assert(chunked.front.equal([3, 4]));
this(Source
source, size_t
chunkSize);
Standard constructor
@property auto
front();
void
popFront();
@property bool
empty();
Input range primitives. Always present.
@property typeof(this)
save();
Forward range primitives. Only present ifSource is a forward range.
@property size_t
length();
Length. Only ifhasLength!Source istrue
auto
opIndex(size_t
index);
typeof(this)
opSlice(size_t
lower, size_t
upper);
Indexing and slicing operations. Provided only ifhasSlicing!Source istrue.
@property auto
back();
void
popBack();
Bidirectional range primitives. Provided only if bothhasSlicing!Source andhasLength!Source aretrue.
struct
EvenChunks(Source) if (isForwardRange!Source && hasLength!Source);
EvenChunks!Source
evenChunks(Source)(Source
source, size_t
chunkCount)
if (isForwardRange!Source && hasLength!Source);
This range splits asource range intochunkCount chunks ofapproximately equal length.Source must be a forward range withknown 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 / chunkCountelements. If
source.length < chunkCount, some chunks will be empty.
chunkCount must not be zero, unless
source is also empty.
Examples:import std.algorithm.comparison : equal;autosource = [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_t
chunkCount);
Standard constructor
@property auto
front();
void
popFront();
@property bool
empty();
@property typeof(this)
save();
Forward range primitives. Always present.
@property size_t
length() const;
Length
auto
opIndex(size_t
index);
typeof(this)
opSlice(size_t
lower, size_t
upper);
@property auto
back();
void
popBack();
Indexing, slicing and bidirectional operations and range primitives. Provided only ifhasSlicing!Source istrue.
auto
slide(Flag!"withPartial" f = Yes.withPartial, Source)(Source
source, size_t
windowSize, size_t
stepSize = 1)
if (isForwardRange!Source);
A fixed-sized sliding window iterationof sizewindowSize over asource range by a customstepSize.
The
Source range must be at least a
ForwardRangeand the
windowSize must be greater than zero.
For
windowSize = 1 it splits the range into single element groups (aka
unflatten)For
windowSize = 2 it is similar to
zip(source,source.save.dropOne).
Parameters:| f | Whether the last element has fewer elements thanwindowSize it should be be ignored (No.withPartial) or added (Yes.withPartial) |
Sourcesource | Range from which the slide will be selected |
size_twindowSize | Sliding window size |
size_tstepSize | Steps between the windows (by default 1) |
Returns:Range of all sliding windows with propagated bi-directionality, forwarding, random access, and slicing.
Examples:Iterate over ranges with windows
import std.algorithm.comparison : equal;assert([0, 1, 2, 3].slide(2).equal!equal( [[0, 1], [1, 2], [2, 3]]));assert(5.iota.slide(3).equal!equal( [[0, 1, 2], [1, 2, 3], [2, 3, 4]]));
Examples:set a custom stepsize (default 1)
import std.algorithm.comparison : equal;assert(6.iota.slide(1, 2).equal!equal( [[0], [2], [4]]));assert(6.iota.slide(2, 4).equal!equal( [[0, 1], [4, 5]]));assert(iota(7).slide(2, 2).equal!equal( [[0, 1], [2, 3], [4, 5], [6]]));assert(iota(12).slide(2, 4).equal!equal( [[0, 1], [4, 5], [8, 9]]));
Examples:Allow the last slide to have fewer elements than windowSize
import std.algorithm.comparison : equal;assert(3.iota.slide!(No.withPartial)(4).empty);assert(3.iota.slide!(Yes.withPartial)(4).equal!equal( [[0, 1, 2]]));
Examples:Count all the possible substrings of length 2
import std.algorithm.iteration : each;int[dstring] d;"AGAGA"d.slide!(Yes.withPartial)(2).each!(a => d[a]++);writeln(d);// ["AG"d:2, "GA"d:2]
Examples:withPartial only has an effect if last element in the range doesn't have the full size
import std.algorithm.comparison : equal;assert(5.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4]]));assert(6.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5]]));assert(7.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5, 6]]));assert(5.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2]]));assert(6.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2]]));assert(7.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5, 6]]));
auto
only(Values...)(return scope Values
values)
if (!is(CommonType!Values == void));
auto
only()();
Assemblevalues into a range that carries all itselements in-situ.
Useful when a single value or multiple disconnected valuesmust be passed to an algorithm expecting a range, withouthaving to perform dynamic memory allocation.
As copying the range means copying all elements, it can besafely returned from functions. For the same reason, copyingthe returned range may be expensive for a large number of arguments.
Parameters:Valuesvalues | the values to assemble together |
Returns:A
RandomAccessRange of the assembled values.
The returned range can be sliced. Its elements can be assigned to if every type in
Values supports assignment from the range's element type.
Examples: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)(Range
range, Enumerator
start = 0)
if (isIntegral!Enumerator && isInputRange!Range);
Iterate overrange with an attached index variable.
Each element is a
std.typecons.Tuple containing the indexand the element, in that order, where the index member is named
indexand the element member is named
value.
The index starts at
start and is incremented by one on every iteration.
OverflowIfrange has length, then it is an error to pass a value forstart so thatstart +range.length is bigger thanEnumerator.max, thus it is ensured that overflow cannot happen.
If
range does not have length, and
popFront is called when
front.index == Enumerator.max, the index will overflow and continue from
Enumerator.min.
Parameters:Rangerange | theinput range to attach indexes to |
Enumeratorstart | the number to start the index counter from |
Returns:At minimum, an input range. All other range primitives are given in the resulting range ifrange has them. The exceptions are the bidirectional primitives, which are propagated only ifrange has length.
ExampleUseful for usingforeach 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:Can start enumeration 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 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);}Examples:void func1(int a,int b);void func2(int a,float b);staticassert(isTwoWayCompatible!(func1,int,int));staticassert(isTwoWayCompatible!(func1,short,int));staticassert(!isTwoWayCompatible!(func2,int,float));void func3(refint a,refint b);staticassert(isTwoWayCompatible!(func3,int,int));staticassert(!isTwoWayCompatible!(func3,short,int));
Policy used with the searching primitives
lowerBound,
upperBound, and
equalRange of
SortedRange below.
Examples:import std.algorithm.comparison : equal;auto a = assumeSorted([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);auto p1 = a.upperBound!(SearchPolicy.binarySearch)(3);assert(p1.equal([4, 5, 6, 7, 8, 9]));auto p2 = a.lowerBound!(SearchPolicy.gallop)(4);assert(p2.equal([0, 1, 2, 3]));
Searches in a linear fashion.
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.
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.
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 thangallop for large ranges. ThebinarySearch policy is used as the last step oftrot,gallop,trotBackwards, and gallopBackwards strategies.
Similar totrot but starts backwards. Use it when confident that the value is around the end of the range.
Similar togallop but starts backwards. Use it when confident that the value is around the end of the range.
enum
SortedRangeOptions: int;
Examples:// create a SortedRange, that's checked strictlySortedRange!(int[],"a < b",SortedRangeOptions.checkStrictly)([ 1, 3, 5, 7, 9 ]);
Assume, that the range is sorted without checking.
All elements of the range are checked to be sorted. The check is performed in O(n) time.
Some elements of the range are checked to be sorted. For ranges with random order, this will almost surely detect, that it is not sorted. For almost sorted ranges it's more likely to fail. The checked elements are choosen in a deterministic manner, which makes this check reproducable. The check is performed in O(log(n)) time.
struct
SortedRange(Range, alias pred = "a < b", SortedRangeOptions opt = SortedRangeOptions.assumeSorted) if (isInputRange!Range && !isInstanceOf!(
SortedRange, Range));
template
SortedRange(Range, alias pred = "a < b", SortedRangeOptions opt = SortedRangeOptions.assumeSorted) if (isInstanceOf!(
SortedRange, 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
SortedRange from an unsorted range
r, use
std.algorithm.sorting.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.
Examples:import std.algorithm.sorting : sort;auto a = [ 1, 2, 3, 42, 52, 64 ];auto r = assumeSorted(a);assert(r.contains(3));assert(!(32in r));auto r1 = sort!"a > b"(a);assert(3in r1);assert(!r1.contains(32));writeln(r1.release());// [64, 52, 42, 3, 2, 1]
Examples:SortedRange could accept ranges weaker than random-access, but itis 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 ischanged concurrently with its corresponding
SortedRange in waysthat break its sorted-ness,
SortedRange will 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 rangeassert(!r.contains(42));// passes although it shouldn't
Examples:SortedRange can be searched with predicates that do not taketwo elements of the underlying range as arguments.
This is useful, if a range of structs is sorted by a member and youwant to search in that range by only providing a value for that member.
import std.algorithm.comparison : equal;staticstruct S {int i; }staticbool byI(A, B)(A a, B b){staticif (is(A == S))return a.i < b;elsereturn a < b.i;}auto r = assumeSorted!byI([S(1), S(2), S(3)]);auto lessThanTwo = r.lowerBound(2);assert(equal(lessThanTwo, [S(1)])); @property bool
empty();
@property auto
save();
@property ref auto
front();
void
popFront();
@property ref auto
back();
void
popBack();
ref auto
opIndex(size_t
i);
auto
opSlice(size_t
a, size_t
b) return scope;
Range primitives.
auto
release() return scope;
Releases the controlled range and returns it.
Examples:import std.algorithm.sorting : sort;int[3] data = [ 1, 2, 3 ];auto a = assumeSorted(data[]);writeln(a);// sort!"a < b"(data[])int[] p = a.release();writeln(p);// [1, 2, 3]
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, 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
SearchPolicy.
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)(V
value)
if (isTwoWayCompatible!(predFun, ElementType!Range, V));
This function searches with policy
sp to find the largest rightsubrange on which
pred(value, x) is
true for all
x(e.g., if
pred is "less than", returns the portion of the rangewith elements strictly greater than
value). The search scheduleand its complexity are documented in
SearchPolicy.
For ranges that do not offer random access,SearchPolicy.linearis the only policy allowed (and it must be specified explicitly lest it exposesuser code to unexpected inefficiencies). For random-access searches, allpolicies are allowed, andSearchPolicy.binarySearch is the default.
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)(V
value)
if (isTwoWayCompatible!(predFun, ElementType!Range, V) && isRandomAccessRange!Range);
Returns the subrange containing all elementse for which both pred(e, value) andpred(value, e) evaluate tofalse (e.g., ifpred is "less than", returns the portion of the range with elements equal tovalue). Uses a classic binary search with interval halving until it finds a value that satisfies the condition, then usesSearchPolicy.gallopBackwards to find the left boundary andSearchPolicy.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.
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)(V
value)
if (isTwoWayCompatible!(predFun, ElementType!Range, V) && isRandomAccessRange!Range && hasLength!Range);
Returns a tupler such thatr[0] is the same as the resultoflowerBound(value),r[1] is the same as the result ofequalRange(value), andr[2] is the same as the result ofupperBound(value). The call is faster than computing all threeseparately. Uses a search schedule similar toequalRange. 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)(V
value)
if (isRandomAccessRange!Range);
Returnstrue if and only ifvalue can be found inrange, which is assumed to be sorted. PerformsΟ(log(r.length))evaluations ofpred.
bool
opBinaryRight(string op, V)(V
value)
if (op == "in" && isRandomAccessRange!Range);
Likecontains, but the value is specified before the range.
Returns a range of subranges of elements that are equivalent according to thesorting relation.
auto
assumeSorted(alias pred = "a < b", R)(R
r)
if (isInputRange!(Unqual!R));
Assumes
r is sorted by predicate
pred and returns thecorresponding
SortedRange!(pred, R) having
r as support.To check for sorted-ness atcost
Ο(n), use
std.algorithm.sorting.isSorted.
Examples:import std.algorithm.comparison : equal;int[] a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];auto p =assumeSorted(a);assert(equal(p.lowerBound(4), [0, 1, 2, 3]));assert(equal(p.lowerBound(5), [0, 1, 2, 3, 4]));assert(equal(p.lowerBound(6), [0, 1, 2, 3, 4, 5]));assert(equal(p.lowerBound(6.9), [0, 1, 2, 3, 4, 5, 6]));
struct
RefRange(R) if (isInputRange!R);
auto
refRange(R)(R*
range)
if (isInputRange!R);
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 isnot copied but is consumed as if it were a reference type.
Notesave works as normal and operates on a new range, so ifsave is ever called on theRefRange, then no operations on the saved range will affect the original.
Parameters:R*range | the range to construct theRefRange from |
Returns:ARefRange. If the given range is a class type (and thus is already a reference type), then the original range is returned rather than aRefRange.
Examples:Basic Example
import 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.ptris &buffer1);assert(wrapped2.ptris &buffer2);assert(wrapped1.ptr !is wrapped2.ptr);assert(buffer1 != buffer2);wrapped1 = wrapped2;//Everything points to the same stuff as before.assert(wrapped1.ptris &buffer1);assert(wrapped2.ptris &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.ptris &buffer1);assert(wrapped2.ptris &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.ptris &buffer1);assert(wrapped2.ptrisnull);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]
pure nothrow @safe this(R*
range);
auto
opAssign(RefRange
rhs);
This does not assign the pointer ofrhs to thisRefRange. Rather it assigns the range pointed to byrhs to the range pointed to by thisRefRange. This is becauseany operation on aRefRange is the same is if it occurred to the original range. The one exception is when aRefRange is assignednull either directly or becauserhs isnull. In that case,RefRange no longer refers to the original range but isnull.
void
opAssign(typeof(null)
rhs);
pure nothrow @property @safe inout(R*)
ptr() inout;
A pointer to the wrapped range.
@property auto
front();
@property auto
front() const;
@property auto
front(ElementType!R
value);
@property bool
empty();
@property bool
empty() const;
@property auto
save();
@property auto
save() const;
auto
opSlice();
auto
opSlice() const;
Only defined ifisForwardRange!R istrue.
@property auto
back();
@property auto
back() const;
@property auto
back(ElementType!R
value);
void
popBack();
Only defined ifisBidirectionalRange!R istrue.
ref auto
opIndex(IndexType)(IndexType
index);
ref auto
opIndex(IndexType)(IndexType
index) const;
Only defined ifisRandomAccessRange!R istrue.
Only defined ifhasMobileElements!R andisForwardRange!R aretrue.
Only defined ifhasMobileElements!R andisBidirectionalRange!R aretrue.
Only defined ifhasMobileElements!R andisRandomAccessRange!R aretrue.
@property size_t
length();
@property size_t
length() const;
alias
opDollar = length;
Only defined ifhasLength!R istrue.
auto
opSlice(IndexType1, IndexType2)(IndexType1
begin, IndexType2
end);
auto
opSlice(IndexType1, IndexType2)(IndexType1
begin, IndexType2
end) const;
Only defined ifhasSlicing!R istrue.
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 bybit, from the least significant bit to the most significant bit.
Parameters:| R | an integralinput range to iterate over |
Rrange | range to consume bit by by |
Returns:ABitwise input range with propagated forward, bidirectional and random access capabilities
Examples:import std.algorithm.comparison : equal;import std.format : format;// 00000011 00001001ubyte[] arr = [3, 9];auto r = arr.bitwise;// iterate through it as with any other rangewriteln(format("%(%d%)", r));// "1100000010010000"assert(format("%(%d%)", r.retro).equal("1100000010010000".retro));auto r2 = r[5 .. $];// set a bitr[2] = 1;writeln(arr[0]);// 7writeln(r[5]);// r2[0] Examples:You can use bitwise to implement an uniform bool generator
import std.algorithm.comparison : equal;import std.random : rndGen;auto rb = rndGen.bitwise;staticassert(isInfinite!(typeof(rb)));auto rb2 = rndGen.bitwise;// Don't forget that structs are passed by valueassert(rb.take(10).equal(rb2.take(10)));
struct
NullSink;
ref auto
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
Examples:import std.csv : csvNextToken;string line ="a,b,c";// ignore the first columnline.csvNextToken(nullSink, ',', '"');line.popFront;// look at the second columnAppender!string app;line.csvNextToken(app, ',', '"');writeln(app.data);// "b"
auto
tee(Flag!"pipeOnPop" pipeOnPop = Yes.pipeOnPop, R1, R2)(R1
inputRange, R2
outputRange)
if (isInputRange!R1 && isOutputRange!(R2, ElementType!R1));
auto
tee(alias fun, Flag!"pipeOnPop" pipeOnPop = Yes.pipeOnPop, R1)(R1
inputRange)
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 of
tee that 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 | IfYes.pipeOnPop, simply iterating the range without ever callingfront is enough to havetee mirror elements tooutputRange (or, respectively,fun). Note that eachpopFront() call will mirror the oldfront value, not the new one. This means that the last value will not be forwarded if the range isn't iterated until empty. IfNo.pipeOnPop, only elements for whichfront does get called will be also sent tooutputRange/fun. Iffront is called twice for the same element, it will still be sent only once. If this caching is undesired, consider usingstd.algorithm.iteration.map instead. |
R1inputRange | The input range being passed through. |
R2outputRange | This range will receive elements ofinputRange progressively as iteration proceeds. |
| fun | This function will be called with elements ofinputRange progressively as iteration proceeds. |
Returns:An input range that offers the elements ofinputRange. Regardless of whetherinputRange is a more powerful range (forward, bidirectional etc), the result is always an input range. Reading this causesinputRange to be iterated and returns its elements in turn. In addition, the same elements will be passed tooutputRange orfun as well.
Examples:import std.algorithm.comparison : equal;import std.algorithm.iteration : filter, map;// Sum values while copyingint[] 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 filterint 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 newValues4assert(equal(newValues4, [2, 5]));writeln(count);// 3
auto
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 therange with the element
e. The element
e must be of a common type withthe element type of the range
r as defined by
std.traits.CommonType.If
n is less than the length of of
r, then
r is returned unmodified.
If
r is a string with Unicode characters in it,
padLeft follows D's rulesabout length for strings, which is not the number of characters, orgraphemes, but instead the number of encoding units. If you want to treat eachgrapheme as only one encoding unit long, then call
std.uni.byGrapheme before calling this function.
If
r has a length, then this is
Ο(1). Otherwise, it's
Ο(r.length).
Parameters:Rr | aninput range with a length, or a forward range |
Ee | element to pad the range with |
size_tn | the length to pad to |
Examples: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)(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 rangewith the element
e. The element
e must be of a common type with theelement type of the range
r as defined by
std.traits.CommonType.If
n is less than the length of of
r, then the contents of
r arereturned.
The range primitives that the resulting range provides depends whether or notrprovides them. Except the functionsback andpopBack, which also requirethe range to have a length as well asback andpopBack
Parameters:Rr | aninput range with a length |
Ee | element to pad the range with |
size_tn | the length to pad to |
Examples: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___")); enum auto
isSomeFiniteCharInputRange(R);
This simplifies a commonly used idiom in phobos for accepting any kind of stringparameter. The type
R can for example be a simple string, chained string using
std.range.chain,
std.path.chainPath or any other input range ofcharacters.
Only finite length character ranges are allowed with this constraint.
This template is equivalent to:
isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R)
Examples:import std.path : chainPath;import std.range : chain;void someLibraryMethod(R)(R argument)if (isSomeFiniteCharInputRange!R){// implementation detail, would iterate over each character of argument}someLibraryMethod("simple strings work");someLibraryMethod(chain("chained"," ","strings"," ","work"));someLibraryMethod(chainPath("chained","paths","work"));// you can also use custom structs implementing a char range