dranges.range
This module defines new ranges or rather, higher-order ranges: ranges acting on ranges
to transform them or present a new view. As far as possible, all higher-order ranges presented in this module
and in dranges.algorithm are "tight wrappers": they are bidirectional if their input
range
is bidirectional,
define opIndex , opIndexAssign , length if it's possible, etc. That way, a good input
range
(for example, a random-access
range
)
will see its properties propagated through a chain of calls.
- Some of these are usual in other languages (Haskell, Scala, Clojure, ...) and are quite useful:
drop , dropWhile , takeWhile , etc.
- Some are extension of standard functions:
delay as a generic way to segment a
range
.
- Some are there just for fun and served as exercices when I wanted to "grok" ranges (like
torus , bounce , emptyRange , once ).
Also, once we admit std.typecons.tuples as a common way to return many values, tuple-returning
ranges can be acted upon in various ways, splicing/shredding/rotating/stitching them. As many ranges and algorithms
presented in this package produce tuples, having a rich way to act upon them permits all kinds of reuse.
License:
Boost License 1.0.
Authors:
Philippe Sigaud
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- size_t
minLength
(R...)(R ranges);
- Return:
the smallest length of all non-infinite ranges passed as inputs. All finite ranges must have a length member.
Example:
auto s = ["a","b","c","d","e","f"];
int[] r = [0,1,2,3];
auto c = repeat(5); // infinite
auto ml = minLength(s,r,c);
assert(ml == 4); // r length
- R
drop
(R)(R r, size_t n);
- Returns a copy of r, with the first n elements dropped. Compare to
popFrontN which affects r. "n" is the first argument as in std.range.take
(which in turn takes this from Haskell)
auto r1 = [0,1,2,3,4,5];
auto d = drop(3, r1);
assert(equal(d, [3,4,5][]));
assert(equal(drop(0, r1), r1)); // drops 0 element -> equal to r1
assert(drop(6, r1).empty); // drops all elements -> empty range
assert(drop(100, r1).empty); // drops more than r1.length -> empty range.
Note:
It's not lazy: it drops all available elements during the call.
- Take!(R)
slice
(R)(R range, int begin, int end);
- To Be Documented.
- R
dropLast
(R)(R range, size_t n);
- Drops the last n elements of range. R must be bidirectional.
- R
dropWhile
(alias pred, size_t step = 1, R)(R r);
- Returns a copy of r, with the first elements dropped as long as pred if verified. Compare to
popFrontWhile which affects r.
The predicate may be unary:
string s = " , abcd efg";
auto d1 = dropWhile!(isOneOf!" ,")(s); // isOneOf!" ," returns true for " " and for ","
assert(d1 == "abcd efg");
auto r1 = [0,1,2,3,3,4,5];
auto d2 = dropWhile!"a<4"(r1); // String predicate
assert(equal(d2, [4,5][]));
Or it can be binary (or ternary, or whatever), possibly a string, as "a<b".
The second (optional) argment is the step, which must be between 1 and the pred's arity. Step's default is 1.
If step == arity, it will drop the entire segment at each step.
Example:
auto d3 = dropWhile!"a<b"(r1); // drop as long as r1 is strictly increasing, testing with binary predicate "a<b"
// It will test [0,1] and drop 0. Then test [1,2] and drop 1, ...
assert(equal(d3, [3,3,4,5][])); // the result begins with a couple for which a<b doesn"t hold
auto d4 = dropWhile!("a<b",2)(r1); // drop as long as r1 is strictly increasing, testing with binary predicate, step of 2
// It will test [0,1] and drop [0,1]. Then test [2,3] and drop it also.
// Then it will drop [3,4] and stop at 5.
assert(equal(d4, [5][]));
auto d5 = dropWhile!"a<b && b<c"(r1); // Growing range, with three args (step defaults to 1)
assert(equal(d5, [2,3,3,4,5][]));
bool foo(int a, int b) { return a*b < 3;}
auto d6 = dropWhile!foo(r1); // binary fun as predicate
assert(equal(d6, [2,3,3,4,5][]));
auto d7 = dropWhile!"true"(r1); // 0-arg predicate, always true -> drops everything -> d7 is empty
assert(d7.empty);
int[] e;
assert(dropWhile!"a<4"(e).empty); // dropping on an empty range: always empty
auto r2 = [1];
auto d8 = dropWhile!"a<b"(r2); // binary predicate: cannot be true applied on [1] -> stops at once
assert(equal(d8, [1][]));
- size_t
popFrontWhile
(alias pred, size_t step = 1, R)(ref R r);
- Advances range while predicate pred is true. It will change range!
The predicate can be a function or a string. If it's a binary (or more) predicate,
it will test as many elements (2, 3...) in one step.
There is an optional argument: the step, defaulting to 1.
Returns:
the number of elements popped.
See Also:
dropWhile.
Example:
auto r1 = [0,1,2,3,4];
auto r2 = [0,1,2,3,4];
auto r3 = [0,1,2,1,0];
auto r4 = [0,1,2,3,2,1];
auto e;
auto a = popFrontWhile!"a<2"(r1); // standard use
assert(equal(r1, [2,3,4][]));
assert(a == 2);
a = popFrontWhile!"a>5"(r2);
assert(equal(r2, [0,1,2,3,4][])); // false predicate, nothing changed
assert(a == 0);
a = popFrontWhile!"a<b"(r3); // binary predicate, step defaults to 1.
assert(equal(r3, [2,1,0][]));
assert(a == 2);
a = popFrontWhile!("a<b",2)(r4); // binary predicate, step of 2.
assert(equal(r4, [2,1][]));
assert(a == 4);
a = popFrontWhile!"a>5"(e); // On an empty range, pops nothing.
assert(a == 0);
- struct
TakeWhile
(alias pred,R) if (isForwardRange!(R) && arity!(pred) > 0);
struct
TakeWhile
(alias pred,R) if (isForwardRange!(R) && arity!(pred) == 0);
TakeWhile!(pred,R)
takeWhile
(alias pred, R)(R range);
- Takes elements from range as long as pred is verified. Usually, the predicate
is a unary function. It can be a binary (or more) function, but only the first element is delivered.
Compared to dropWhile and such, there is no step option.
Example:
auto r1 = [0,1,2,3,4,4,6,1,1,1,0];
auto tw1 = takeWhile!"a<6"(r1); // unary predicate
assert(equal(tw1, [0,1,2,3,4,4][]));
auto tw2 = takeWhile!"a<b"(r1); // binary predicate
assert(equal(tw2, [0,1,2,3][]));
bool foo(int a, int b, int c) {return a<b && b<c;} // ternary predicate
auto tw3 = takeWhile!foo(r1);
assert(equal(tw3, [0,1,2][]));
- R
tail
(R)(R range);
- Like the eponymous function found in all functional languages.
Returns the range minus its first element. If range is empty
it just returns it.
Example:
auto r = [0,1,2,3];
auto tails = [r][];
foreach(i; 0..5) {
r = tail(r);
tails ~= r;
}
assert(equal(tails, [[0,1,2,3], [1,2,3], [2,3], [3], [], [] ][]));
- Tails!(R)
tails
(R)(R range);
- Returns the successive application of tail on a range, up to the empty range.
If the input range is empty,
tails
is empty.
Example:
auto r = [0,1,2,3];
auto t = tails(r);
assert(equal(t, [[1,2,3], [2,3], [3], [] ][]));
int[] e;
auto t1 = tails(r[0..1]);
assert(equal(t1, [e][])); // One element -> [] and then stops.
auto te = tails(e);
assert(te.empty); // No element -> empty
- Heads!(R)
heads
(R)(R range);
- Takes n successive elements from range, with n going from 0 (an empty range) to range.length included.
It's more efficient for a range with a length:
heads
knows when to stop. For a range
that doesn"t know its length,
heads
has to calculate for each new head if it's the entire range.
Example:
auto r = [0,1,2,3];
auto h = heads(r);
assert(equal(h, [[], [0], [0,1], [0,1,2], [0,1,2,3] ][])); // from [] up to r itself.
auto f = filter!"a<10"(r); // equivalent to r, but doesn"t know its own length
auto hf = heads(f);
foreach(elem; hf) { // Compare it to f
assert(equal(elem, h.front)); // They are indeed the same
h.popFront;
l++; // accumulate length
}
popFrontN(hf, l); // get rid of all elements?
assert(hf.empty); // Yes, it's empty
- Chain!(typeof(take(R1.init,0)),R2,R1)
insertAt
(R1, R2)(size_t n, R1 range1, R2 range2);
Chain!(typeof(take(R.init,0)),E[],R)
insertAt
(R, E)(size_t n, R range, E element);
- Inserts an element or an entire range into range1 at position n. Position 0 means before range1. If position >= range1.length,
element/range2 is just concatenated at the end.
Examples:
auto r1 = [0,1,2,3];
auto m = map!"a*a"(r1);
auto ia0 = insertAt(0, r1, m);
auto ia2 = insertAt(2, r1, m);
auto ia10 = insertAt(10, r1, m);
assert(equal(ia0, [0,1,4,9, 0,1,2,3][]));
assert(equal(ia2, [0,1, 0,1,4,9, 2,3][]));
assert(equal(ia10,[0,1,2,3, 0,1,4,9][]));
auto ia1 = insertAt(1, r1, 99);
assert(equal(ia1, [0,99,1,2,3][]));
- Tuple!(R,R)
cutAt
(R)(size_t index, R range);
Tuple!(ElementType!(R)[],R)
cutAt
(R)(size_t index, R range);
- Cuts a range in two parts, separating them at index index. It returns the parts as a tuple.
The second part will begin with range[index].
If slicing is available, it will use it and return a Tuple!(R,R). If not, it iterates the range
and returns a Tuple!( (ElementType!R)[], R ).
auto r1 = [0,1,2,3,4,5]; // Cutting an array (range with length and slicing)
auto c1 = cutAt(3, r1);
assert(first(c1) == [0,1,2][]); // first part
assert(second(c1) == [3,4,5][]); // second part
assert(equal(r1, [0,1,2,3,4,5][])); // r1 is unchanged
auto c2 = cutAt(0, r1); // Cuts at position 0
assert(first(c2).empty);
assert(second(c2) == r1);
auto c3 = cutAt(1000, r1); // Cuts farther than range.length. No exception is thrown, it returns (range, [])
assert(first(c3) == r1);
assert(second(c3).empty);
auto c4 = cutAt(5, stutter(3, r1)); // Cutting a range with a length but no slicing
assert(equal(first(c4), [0,0,0,1,1][]));
assert(is(typeof(first(c4)) == int[])); // The first part is an int[] (nothing more can be done generically)
assert(equal(second(c4), [1,2,2,2,3,3,3,4,4,4,5,5,5][]));
assert(is(typeof(second(c4)) == Stutter!(int[]))); // The second part is still a Stutter
- Tuple!(ElementType!(R)[],R)
cutWhen
(alias pred, R)(R range);
- Iterates on range as long as pred(elem) is verified. Then returns a tuple containing the first part as an array
and the second part as a truncated range.
Note:
It cannot return a Tuple!(R,R) as sometimes we cannot construct a R from the popped elements: if
R is a Map!() for example, there is no easy and generic way for the first part to be created as a Map also.
Example:
auto r1 = [0,1,2,3,4];
auto cut = cutWhen!"a<3"(r1);
assert(equal(cut.field[0], [0,1,2][]));
assert(equal(cut.field[1], [3,4][]));
assert(equal(r1, [0,1,2,3,4][])); // r1 is unchanged
- struct
Knit
(R...) if (allSatisfy!(isForwardRange,R));
- An alternate (simpler) form of std.range.zip: produce a range of std.typecons.Tuples from
a variadic list. Used mainly to interact with other ranges, as Tuples are more generic
than Proxy. It will stops as soon as one of the input ranges is empty.
If only one range is given as input, it will still produce a tuple. So knit(r) != r
It's an extensible range: opIndex, back, popBack, length, opIndex, opIndexAssign and opSlice
are defined if possible (though some improvement could be made on dealing with infinite ranges).
If all ranges are infinite,
Knit
sees it and defines itself as infinite also.
Note:
it unqualifies the element types (ie: a string has immutable(char) for element type,
but the tuple will use only char).
Example:
auto r1 = [0,1,2,3,4,5];
auto r2 = [3.14, 2.78, 0.25, -1.0, 0.0];
auto s = ["a","b","c","d"];
auto k = knit(r1,s,r2);
assert(k.front == tuple(0,"a",3.14));
assert(equal(k, [tuple(0,"a",3.14), tuple(1,"b",2.78), tuple(2,"c",0.25), tuple(3,"d",-1.0)][]));
// Defining more operations:
assert(k.length == s.length); // length is defined
// back and popBack are defined
assert(equal(retro(k),
[tuple(3,"d",-1.0), tuple(2,"c",0.25), tuple(1,"b",2.78), tuple(0,"a",3.14)][]));
assert(k[2] == tuple(2, "c", 0.25)); // opIndex is defined
assert(equal(k[1..3], [tuple(1, "b", 2.78), tuple(2, "c", 0.25)][])); // opSlice is defined
// More operations impossible:
auto m = map!"a*a"(r2); // no .length, .back, ... (except if you use phobos_extension.d)
auto k2 = knit(r1, r2, m);
assert(k2.front == tuple(0, 3.14, 3.14*3.14));
assert(!is(typeof(k2.length))); // so no .length defined for k2. Nor .back, etc.
// OpIndexAssign: needs ranges which are assignable (ie: no string, map...)
auto k3 = knit(r1, r2);
k3[2] = tuple(0, 0.0);
assert(equal(k3, [tuple(0, 3.14), tuple(1,2.78), tuple(0,0.0), tuple(3, -1.0), tuple(4, 0.0)][]));
// On empty ranges: empty
assert(knit(r1, emptyRange!int, r2).empty);
Limitation:
It cannot be sorted, as front/back do not return by ref. std.range.Zip sort of cheats, inserting a
special proxySwap method inside its Proxy, which is treated specially by std.algo.sortImpl. If you
need a
Knit
to be sorted, you can call sortAsArray on it.
Example:
auto ak = sortAsArray(k);
- StitchType!(R)
stitch
(R...)(R ranges);
- Create a tuple-returning range from a variadic list of tuple-returning ranges by outputting their elements
all in parallel in one tuple.
(it's a bit like Knit, but it acts only on tuple-ranges and with auto-flattening of the tuples).
Tuple-returning ranges are knit, tmap, tfilter, segment, delay, ...
and obviously any map-like range with a tuple-returning function: map, comprehension, parallelComprehension, ...
Examples:
auto r1 = [0,1,2,3,4,5];
auto r2 = [0.1, 0.2, 0.3, 0.4];
auto r3 = ["abc", "def", "", "g"];
auto r4 = ["a", "b", "c", "d", "e", "f"];
auto k1 = knit(r1,r2); // returns Tuple!(int, double)
auto k2 = knit(r3,r4); // returns Tuple!(string, char)
auto tf = tfilter!"b<2"(r3,r1); // returns Tuple!(string, int);
auto s = stitch(k1,k2,tf); // returns Tuple!(int,double,string,char,string,int)
assert(s.front == tuple(0, 0.1, "abc", "a", "abc", 0));
s.popFront;
assert(s.front == tuple(1, 0.2, "def", "b", "def", 1));
s.popFront;
assert(s.empty); // tf stops there because now r1's elements are not less than 2. So s stops there also.
- Map!(naryFun!(rotateTuple!(n,ElementType!(R).Types)),R)
twist
(int n, R)(R range);
- Takes a tuple-producing range and rotate each tuple by n positions. If n>0,
it rotates to the left: takes the first n fields and put them at the end.
If n<0 it rotates to the right: takes the last n fields and put them at the beginning.
Example:
auto r1 = [0,1,2,3,4];
auto r2 = [3.14, 2.78, 1.414];
auto s = ["a","b","c","d","e","f"];
// Let's create a tuple-returning range.
auto k = knit(r1,r2,s);
assert(is(ElementType!(typeof(k)) == Tuple!(int,double,string)));
assert(equal(k, [tuple(0,3.14,"a"), tuple(1,2.78,"b"), tuple(2,1.414,"c")][]));
auto rot1 = twist!1(k);
assert(is(ElementType!(typeof(rot1)) == Tuple!(double,string,int)));
assert(equal(rot1, [tuple(3.14,"a",0), tuple(2.78,"b",1), tuple(1.414,"c",2)][]));
auto rot_1 = twist!(-1)(k);
assert(is(ElementType!(typeof(rot_1)) == Tuple!(string,int,double)));
assert(equal(rot_1, [tuple("a",0,3.14), tuple("b",1,2.78), tuple("c",2,1.414)][]));
- Map!(naryFun!(reverseTuple!(ElementType!(R).Types)),R)
reverse
(R)(R range);
- Takes a tuple-producing range and
reverse
each tuple: the first field
becomes the last one, and so on.
Note:
I"d like another name. twist?
Example:
auto r1 = [0,1,2,3,4];
auto r2 = [3.14, 2.78, 1.414];
auto s = ["a","b","c","d","e","f"];
auto k = knit(r1,r2,s);
assert(is(ElementType!(typeof(k)) == Tuple!(int,double,string)));
assert(equal(k, [tuple(0,3.14,"a"), tuple(1,2.78,"b"), tuple(2,1.414,"c")][]));
auto rev = reverse(k);
assert(is(ElementType!(typeof(rev)) == Tuple!(string,double,int)));
assert(equal(rev, [tuple("a",3.14,0), tuple("b",2.78,1), tuple("c",1.414,2)][]));
- SpliceType!(n,R1,R2)
splice
(size_t n, R1, R2)(R1 range1, R2 range2);
- Takes a tuple-producing range, range1, and another range, range2 and creates a new tuple-returning range
which inserts the elements of range2 at position n into range1's elements. As for an array, index 0
is before all other elements, etc.
Example:
auto r1 = [0,1,2,3,4];
auto s = ["a","b","c","d","e","f"];
auto k = knit(r1,s); // k returns Tuple!(int,string)
auto r2 = [-2.1, 0.0, 3.14];
auto spl = splice!1(k,r2); // splices r2 in the middle of k's elements.
assert(is(ElementType!(typeof(spl)) == Tuple!(int,double,string)));
assert(equal(spl, [tuple(0,-2.1,"a"), tuple(1,0.0,"b"), tuple(2, 3.14, "c")][]));
auto spl2 = splice!0(spl,k); // splices k before all spl's elements.
assert(is(ElementType!(typeof(spl2)) == Tuple!(Tuple!(int,string), int,double,string))); // non-flattening
assert(equal(spl2, [tuple(tuple(0,"a"), 0,-2.1,"a"), tuple(tuple(1,"b"),1,0.0,"b"), tuple(tuple(2,"c"),2, 3.14, "c")][]));
As std.typecons.Tuple is not auto-flattening, you can
splice
tuple-producing ranges into one another.
Example:
auto spl2 = splice!0(spl,k); // splices k before all spl's elements.
assert(is(ElementType!(typeof(spl2)) == Tuple!(Tuple!(int,char), int,double,char))); // non-flattening
assert(equal(spl2, [tuple(tuple(0,"a"), 0,-2.1,"a"), tuple(tuple(1,"b"),1,0.0,"b"), tuple(tuple(2,"c"),2, 3.14, "c")][]));
- ShredType!(array,R)
shred
(alias array, R)(R range);
- Takes a tuple-producing range and an array of indices ([0,1,2] for example, meaning "first, second and third fields")
and returns a tuple-producing range whose elements" fields are those indicated by array.
The indices can be repeated, omitted, put in any order and the array can be longer than the input tuple ([0,1,2,3,2,1,3,1,0]).
Example:
auto r1 = [0,1,2,3,4];
auto r2 = [3.14, 2.78,1.414];
string s = "abcdef";
auto k = knit(r1,r2,s);
auto shr1 = shred!([1,0])(k); // inverting the order
assert(equal(shr1, [tuple(3.14,0), tuple(2.78,1), tuple(1.414,2)][]));
auto shr2 = shred!([1])(k); // taking only one field
assert(equal(shr2, [tuple(3.14), tuple(2.78), tuple(1.414)][]));
auto shr3 = shred!([2,0,1,1])(k); // repating some fields
assert(equal(shr3, [tuple("a",0,3.14,3.14), tuple("b",1,2.78,2.78), tuple("c",2,1.414,1.414)][]));
auto shr4 = shred!([1,2,0])(shr3); // re-inverting the fields
assert(equal(shr4, k)); // this re-creates k
- ShredType!(n,R)
shred
(size_t n, R)(R range);
- Another version of
shred
that takes only one index. It extracts the corresponding field
from a tuple-producing range's elements and returns that as a range, 'un-tuplified'.
That is, where
shred
!([1])(k) will produce a tuple-returning range (one-element tuples,
but tuples nonetheless),
shred
!1(k) will produce a standard range.
Example:
auto r1 = [0,1,2,3,4];
auto r2 = [3.14, 2.78,1.414];
auto s = ["a","b","c","d","e","f"];
auto k = knit(r1,r2,s);
auto shr1 = shred!([1,0])(k); // inverting the order
assert(equal(shr1, [tuple(3.14,0), tuple(2.78,1), tuple(1.414,2)][]));
auto shr2 = shred!([1])(k); // taking only one field
assert(equal(shr2, [tuple(3.14), tuple(2.78), tuple(1.414)][]));
auto shr3 = shred!([2,0,1,1])(k); // repating some fields
assert(equal(shr3, [tuple("a",0,3.14,3.14), tuple("b",1,2.78,2.78), tuple("c",2,1.414,1.414)][]));
auto shr4 = shred!([1,2,0])(shr3); // re-inverting the fields
assert(equal(shr4, k)); // this re-creates k
auto shr5 = shred!1(k);
assert(equal(shr5, r2));
- TMapType!("a",R)
untuplify
(R)(R range);
- Takes a tuple-producing range whose elements are 1-element tuples (mainly, this can happen as the result
of some extracting, shredding, filtering, etc.) and "
untuplify
" it, extracting the tuples values.
Example:
auto r1 = [0,1,2,3,4];
auto k = knit(r1);
assert(equal(k, [tuple(0), tuple(1), tuple(2), tuple(3), tuple(4)]));
auto u = untuple(k);
assert(equal(u, r1)); // u is [0,1,2,3,4]
See Also:
shred.
- struct
Transverse
(R...) if (allSatisfy!(isForwardRange,R) && CompatibleRanges!(R));
Transverse!(R)
transverse
(R...)(R ranges);
- A range that iterates alternatively on the ranges given at creation. It's related
to std.range.Transversal, but will iterated on all "columns" and will stop
as soon as one of them is empty.
Example:
auto r1 = [0,1,2,3,4];
auto r2 = repeat(5);
auto r3 = [-2.0, -1.0, 0.0, 1.0];
auto t = transverse(r1, r2, r3);
assert(is(ElementType!(typeof(t)) == double)); // double is the common type for (int, int, double)
assert(equal(t, [0.0,5,-2,1,5,-1,2,5,0,3,5,1,4,5][])); // after 4,5, (the internal copy of) r3 is exhausted.
auto t2 = transverse(r1, emptyRange!double, emptyRange!short);
assert(is(ElementType!(typeof(t2)) == double));
assert(equal(t2, [0][])); // 0, then stops because it reaches emptyRange!double
- Transverse!(B,Cycle!(S))
interleave
(B, S)(B bigRange, S smallRange);
- Simple use of transverse.
Alternates between bigRange and smallRange, rolling smallRange into a cycle.
Example:
auto i1 = interleave("abcdef", ","); // -> a,b,c,d,e,f,
auto i2 = interleave("abcdef", ",;."); // -> a,b;c.d,e;f.
auto r1 = [0,1,2,3,4];
auto i3 = interleave(r1,r1);
assert(equal(i3, [0,0,1,1,2,2,3,3,4,4][]));
- Knit!(TypeNuple!(R,segLength))
segment
(size_t segLength, R)(R range);
- Cuts a range into segments of length segLength. segLength must be stricly positive.
To be compatible with other ranges, it produces tuples, not arrays. It's based on Knit
so if the original range defines back/popBack, length, opIndex or opIndexAssign, it will do so also.
It's used heavily internally by all ranges mapping a function or a predicate on a range.
See Also:
delay, parallel.
Examples:
auto r1 = [0,1,2,3,4,5];
auto s = segment!2(r1);
assert(equal(s, [tuple(0,1), tuple(1,2), tuple(2,3), tuple(3,4), tuple(4,5)][]));
assert(s.length == 5); // .length
// back/popBack:
assert(equal(retro(s), retro([tuple(0,1), tuple(1,2), tuple(2,3), tuple(3,4), tuple(4,5)][])));
assert(s[3] == tuple(3,4)); // opIndex
s[3] = tuple(0,0); // opIndexAssign
assert(s[2] == tuple(2,0)); // it affects its neighbors.
assert(s[4] == tuple(0,5));
assert(r1 == [0,1,2,0,0,5][]); // affects r1 back (no .dup internally)
auto st = ["a","b","c","d","e","f"]; // initial example with a string. Change due to DMD 2.041
auto s2 = segment!3(st);
assert(s2.front == tuple("a","b","c"));
r1 = [0,1,2,3,4,5]; // regenerates r1
auto s3 = segment!1(r1);
assert(equal(s3, [tuple(0), tuple(1), tuple(2), tuple(3), tuple(4), tuple(5)][]));
auto r2 = map!"a*a"(r1);
auto s4 = segment!2(r2); // On a forward range
assert(equal(s4, [tuple(0,1), tuple(1,4), tuple(4,9), tuple(9,16), tuple(16,25)][]));
- Knit!(TypeNuple!(R,array.length))
delay
(alias array, R)(R range);
- A generalization of segment: given an array of indices (as template argument) and a range,
it will produce the corresponding "extracts" (disjointed segments, as tuples).
segment!n(r) is equivalent to
delay
!([0,1,2, ...,n])(r).
But
delay
is much more powerful than segment.
delay!([2,1,0])(r); // You can invert the values
delay!([4,9,2,0])(r); // take them from everywhere in the range
delay!([0,0,1])(r); // repeat some values
See Also:
segment, parallel.
Example:
auto r1 = [0,1,2,3,4,5];
auto d = delay!([0,1])(r1); // will iterate on the pair ([0,1,2,3,4,5], [1,2,3,4,5]).
// It's equivalent to segment!2(r1)
assert(equal(d, [tuple(0,1), tuple(1,2), tuple(2,3), tuple(3,4), tuple(4,5)][]));
assert(d.length == 5); // .length
// back/popBack:
assert(equal(retro(d), retro([tuple(0,1), tuple(1,2), tuple(2,3), tuple(3,4), tuple(4,5)][])));
auto d2 = delay!([1,0])(r1); // inverting
assert(equal(d2, [tuple(1,0), tuple(2,1), tuple(3,2), tuple(4,3), tuple(5,4)][]));
auto d3 = delay!([0])(r1); // one element
assert(equal(d3, [tuple(0), tuple(1), tuple(2), tuple(3), tuple(4), tuple(5)][]));
auto d4 = delay!([4,1,3,2])(r1); // disjoint extracts
assert(d4.front == tuple(4,1,3,2));
assert(d4.length == 2); // d4 is [(4,1,3,2),(5,2,4,3)]
auto d5 = delay!([0,0,1])(r1); // repeated values
assert(d5.front == tuple(0,0,1));
auto d6 = delay!([9,0])(r1);
assert(d6.empty);
int[] e;
assert(delay!([0,1])(e).empty);
- Knit!(TypeNuple!(R,n))
parallel
(size_t n, R)(R range);
- Another "delay" cousin: takes a number (as template argument) and a range, and produces
a "knit" of n times the same range in
parallel
.
See Also:
segment, delay.
Example:
auto r1 = [0,1,2,3,4,5];
auto p = parallel!4(r1);
assert(equal(p, [tuple(0,0,0,0), tuple(1,1,1,1), tuple(2,2,2,2), tuple(3,3,3,3), tuple(4,4,4,4), tuple(5,5,5,5)][]));
- struct
Concat
(R) if (isRangeOfRanges!(R));
Concat!(R)
concat
(R)(R range);
R
concat
(R)(R range);
- A simple wrapper to concatenate elements of a range of ranges.
It's equivalent to flatten!1(range), but it's only a forward range.
On a simple range, it has no effect (it's the identity function).
Example:
int[][] r1 = [[0,1,2], [3,4], [5]];
auto c = concat(r1);
assert(equal(c, [0,1,2,3,4,5][])); // from an int[][] to an int[]
assert(equal(retro(c), retro([0,1,2,3,4,5][]))); // bidir range
auto r2 = [0,1,2,3];
auto ror = map!"take(a+1, repeat(a))"(r2); // Equivalent to [[0], [1,1], [2,2,2], [3,3,3,3]]
assert(equal(concat(ror), [0,1,1,2,2,2,3,3,3,3][]));
string sentence = "the quick brown fox jumped over the lazy dog.";
auto words = split(sentence); // a string[], so also a immutable(char)[][]
auto sentence2 = concat(words);
assert(array(sentence2) == "thequickbrownfoxjumpedoverthelazydog.");
assert(equal(concat(c), c)); // c is a simple range, concat(c) has no effect.
BUG:
doesn"t play so nice with retro. retro.popBack calls concat.popFront, but it seems to have no effect.
- FlattenType!(n,R)
flatten
(size_t n = size_t.max, R)(R range);
- Flatten a range of ranges (of ranges, etc.) n levels deep. n==0 does nothing. n == 1 is just concat(range),
n == 2 is concat(concat(range)), etc. The default is to go for the maximum flattening.
Example:
int[][] r1 = [[0,1,2], [3,4], [5]];
auto f = flatten(r1);
assert(equal(f, [0,1,2,3,4,5][]));
int[][][] r2 = [r1, [[6]], r1];
auto f2 = flatten!2(r2);
assert(equal(f2, [0,1,2,3,4,5,6,0,1,2,3,4,5][]));
assert(equal(retro(f2), [5,4,3,2,1,0,6,5,4,3,2,1,0][])); // bidir range
auto f3 = flatten!0(r2); // No flattening -> range unchanged.
assert(equal(f3, r2));
auto f4 = flatten(r2); // go for max flattening. Equals to flatten!2(r2)
assert(equal(f2, f4));
auto f5 = flatten!1(r2); // flatten one level
assert(equal(f5, [[0,1,2], [3,4], [5], [6], [0,1,2], [3,4], [5]][]));
assert(equal(retro(f5), [[5], [3,4], [0,1,2], [6], [5], [3,4], [0,1,2]][]));
- void
truncate
(R...)(ref R r);
- Small helper function: given a variadic list of ranges, truncates them to the shortest range's length.
This will modify the input ranges!
Example:
auto r1 = [0,1,2,3];
string s = "abcdefghijk";
truncate(r1, s);
assert(r1.length == s.length);
assert(equal(r1, [0,1,2,3][]));
assert(s == "abcd");
BUG:
Does not work since DMD 2.04x, when strings were modified. Maybe by using byCodePoint or somesuch?
- Knit!(Numbers,R)
indexed
(R)(R range);
- Emulates the standard Clojure/Python/Ruby function: given a range r,
it will produce an
indexed
range, with elements tuples(index, element).
If possible,
indexed
defines back, popBack, length, opIndex and opSlice.
auto s = ["a", "b", "c", "d", "e", "f"];
auto e = indexed(s); // (0,"a"), (1,"b"), (2,"c"), (3,"d"), (4, "e"), (5, "f")
assert(e.front == tuple(0, "a"));
assert(e.length == s.length);
e.popFront;
assert(e.front == tuple(1, "b"));
assert(e[3] == tuple(4, "e")); // opIndex
assert(e.back == tuple(5, "f")); // back
- struct
Numbers
;
Numbers
numbers
(int to = (int).max);
Numbers
numbers
(int from, int to, int step = 1);
- A small range to get the numbers from from to to (open at to, it's never reached) with step step.
0-, 1-, 2- and 3-args version exist, see the examples.
Examples:
auto n0 = numbers(); // -> 0,1,2,3,4, ... ,int.max
auto n1 = numbers(10); // -> 0,1,2,3,4,5,6,7,8,9
auto n2 = numbers(5,10); // -> 5,6,7,8,9
auto n3 = numbers(1,10,3); // -> 1,4,7
auto n4 = numbers(1,10,100); // -> 1
auto n5 = numbers(20,10, -1); // -> 20,19,18,17,16,15,14,13,12,11
auto n6 = numbers(20,10); // step defaults to 1 -> empty range
assert(n1[3] == 3); // opIndex
assert(equal(n1[5..10], n2)); // opSlice
assert(equal(retro(n1), [9,8,7,6,5,4,3,2,1,0][])); // It's a bidirectional range
- struct
Numberz
(T);
Numberz!(T)
numberz
(T)(T to);
Numberz!(T)
numberz
(T)(T from, T to, T step);
- A templated struct for a range of numbers. It can be used with std.bigint, for example.
There is no argument-less version: numberz!BigInt() does not exists (as there is no BigInt.max).
auto n1 = numberz(BigInt("1000000000000000"), BigInt("2000000000000000"));
// -> 1000000000000000, 1000000000000001, 1000000000000002, 1000000000000003, ..., 1000000000000010
assert(n1[3] == BigInt("1000000000000003")); // opIndex with standard index
assert(n1[BigInt("500000000000000")] == BigInt("1500000000000000")); // Indexed with BigInt too
assert(n1.length == BigInt("1000000000000000")); // .length returns a BigInt
- struct
NaturalNumbers
;
- Simple range producing all natural numbers, alternating
between positive and negative numbers: 0, 1, -1, 2, -2, 3, -3, ...
- struct
EmptyRange
(T);
EmptyRange!(T)
emptyRange
(T)();
- An empty range. Its element type is parameterized by T, to enable compatibility
(for ranges which check front's type).
auto e = emptyRange!int; //uses the helper function. Otherwise: auto e = EmptyRange!int();
assert(e.empty);
assert(e.length == 0);
assert(asString(e) == "");
assert(is(ElementType!(typeof(e)) == int));
assert(isInputRange!(typeof(e)));
assert(isForwardRange!(typeof(e)));
assert(isBidirectionalRange!(typeof(e)));
assert(isRandomAccessRange!(typeof(e)));
assert(hasLength!(typeof(e)));
assert(hasSlicing!(typeof(e)));
- struct
Once
(T);
Once!(T)
once
(T)(T value);
- A one-element range. This element is defined at creation and will be produced once. The range is then empty.
auto e = once(1.1);
assert(e.front == 1.1);
assert(e.length == 1);
assert(asString(e) == "1.1");
e.popFront;
assert(e.empty);
- struct
Pi
;
Pi
piDigits
();
- An infinite range giving the digits of pi, in base 10 (as ints).
Example:
auto pi = take(piDigits, 10);
assert(equal(pi, [3,1,4,1,5,9,2,6,5,3]));
It has a reasonable speed up to a few thousands digits, but slows down sharply afterwards.
On my machine: 1000 digits: 100 ms, 3000 digits: 300 ms, 10000 digits: 2.4 s, 30000 digits: 22 s
- struct
InfiniteBiDir
(R1,R2) if (isForwardRange!(R1) && isForwardRange!(R1) && isInfinite!(R1) && isInfinite!(R2) && !isBidirectionalRange!(R1) && !isBidirectionalRange!(R2) && CompatibleRanges!(R1,R2));
- It's an infinite bidirectional range: a range that has infinitely many values,
but knows its end:
n0 n1 n2 ... (infinitely many values) ... m2 m1 m0
It's in fact a completly standard infinite bidirectional range, modelized with two (non-bidir) infinite ranges.
- struct
ReplicateRange
(R) if (isForwardRange!(R));
- Replicates a range n times. back, popBack, length and opIndex will be defined
if the subjacent R permits it.
Example:
auto r1 = [0,1,2,3];
auto r2 = replicateRange(r1, 3);
assert(equal(r2, [0,1,2,3,0,1,2,3,0,1,2,3][]));
assert(r2.length == r1.length * 3);
assert(r2[3] == 3);
r2.popFront;
r2.popBack;
assert(equal(r2, [1,2,3,0,1,2,3,0,1,2][])); // You can truncate it at both ends
assert(r2.length == r1.length * 3 - 2); // length update
assert(r2[3] == 0); // Indexing is always 0-based, of course.
assert(replicateRange(r1, 0).empty); // if n == 0, replicateRange is an empty range
assert(equal(replicateRange(r1, 1), r1));
- struct
Stutter
(R) if (isForwardRange!(R));
Stutter!(R)
stutter
(R)(uint n, R range);
- Repeat n times each element of a range. If the input is a bidirectional range,
stutter will also be one (defines back and popBack), the same for a random-access range.
Also, if input has a length, stutter will have one.
Example:
auto r1 = [0,1,2,3];
string s = "abc";
auto st1 = stutter(r1, 3);
auto st2 = stutter(s, 2);
assert(st1.length == r1.length * 3);
assert(equal(st1, [0,0,0,1,1,1,2,2,2,3,3,3][]));
assert(equal(st2, "aabbcc")); // Works on strings, too.
//
st1.popFront;
st1.popBack;
assert(equal(st1, [0,0,1,1,1,2,2,2,3,3][]));
assert(st1.length == r1.length * 3 - 2); // length update
assert(equal(retro(st1), [3,3,2,2,2,1,1,1,0,0][])); // Bidirectional range
assert(st1[2] == 1); // Random-access range
//
assert(stutter(r1, 0).empty); // stutter(_,0) -> empty
auto e = emptyRange!int;
assert(stutter(e, 3).empty); // stutter(empty, n) -> empty
assert(equal(stutter(r1,1), r1)); // stutter(r,1) -> r
- Concat!(Map!(unaryFun!(Format!("array(replicate(%s,a))",n)),R))
stutter2
(size_t n, R)(R r);
- Another version, based on flatMap.
flatMap!(Format!("array(replicate(%s,a))", n))(r);
It's a one-liner, but it's a forward range, no more (no opIndex, back, etc)
- struct
Extremities
(R) if (isBidirectionalRange!(R));
Extremities!(R)
extremities
(R)(R range);
- A forward range which alternates between input's two extremities. It's a kind of dual to std.range.radial.
Example:
auto r1 = [0,1,2,3,4,5,6];
auto ext = extremities(r1);
assert(ext.length == r1.length); // length is defined
assert(equal(ext, [0,6,1,5,2,4,3][]));
assert(extremities(emptyRange!int).empty); // extremities on an empty range -> empty
- Cycle!(Chain!(R,Retro!(R)))
bounce
(R)(R range);
- Iterate on a bidirectional range by going back and forth between its extremities.
Example:
auto r = [0,1,2,3];
auto b = bounce(r);
assert(equal(take(b, 10), [0,1,2,3,2,1,0,1,2,3][]));
auto r2 = [1];
assert(equal(take(bounce(r2), 10), replicate(10, 1))); // 1,1,1,1,...
- struct
Without
(R1,R2);
Without!(R1,R2)
without
(R1, R2)(R1 range1, R2 range2, bool cyclic = false);
- Produces a range with elements of r1 without those of r2. Equivalent to Haskell (\\).
By default, each element of r2 is used only once. With the boolean option cyclic set to true,
you get without(r1, cycle(r2)) and all occurences of an element of r2 in r1 are dropped.
Example:
auto r1 = [0,1,2,3,4,5,6,2,3,4];
without(r1, [2,3,4][]); // [0,1,5,6,2,3,4][] getting rid of 2,3,4. Those at the end are still there
without(r1, [2,3,4,2,3][]); // [0,1,5,6,4][] 2,3 at the end are eliminated
without(r1, [2,3,4][], true); // [0,1,5,6][] eliminate all 2, 3 and 4 from r1.
without("banana", "a", true); // "bnn"
without(r1, numbers(), true); // (int[]).init (empty range) all are numbers eliminated from r1
r1.without([2,3,4][]); // For arrays, you can also use an infix syntax
"banana".without("a", true);
- struct
AsSet
(R) if (isForwardRange!(R));
struct
AsSet
(R : Cycle!(U),U);
struct
AsSet
(R : Repeat!(U),U);
AsSet!(R)
asSet
(R)(R range);
- Helper struct to transform a range into a set. Each value is memoized when first output
and then filtered.
AsSet
has no way to know if all values have been created, so on an infinite range
it may never stop, looking for a nth new value which will never come.
AsSet
does know
about std.range periodic ranges (Cycle and Repeat) and extract the internal value.
Example:
asSet([0,1,2,2,3,3,4,5,5,6,9,1,2,4,9][]); // 0,1,2,3,4,5,6,9, OK.
asSet(cycle([4,5,6][])); // 4,5,6
- struct
Cache
(R) if (isForwardRange!(R));
Cache!(R)
cache
(R)(R r);
- Simple wrapper range to cache the front and back elements of another range.
- void
forEach
(alias fun, R)(R range);
- To iterate on range using a function with side-effects. It doesn"t use the return values,
so it acts only through fun's side-effects. Mainly used to print a range.
auto r1 = [0,1,2,3,4];
forEach!(write)(r1); // writes "01234"
int sum;
forEach!((int a){sum += a;})(r1);
assert(sum == reduce!"a+b"(r1)); // sum contains 0+1+2+3+4
- string
asString
(R)(R range, string sep = "");
string
asString
(R)(R range, string pre, string sep, string post);
- Greedily iterates on range and returns a string with all range elements
separated by sep. An overload exists, with three string parameters controlling
the beginning of the string, the separator and the end.
Example:
auto r1 = [0,1,2,3];
string r2 = "abcdef";
assert(asString(r1) == "0123");
assert(asString(r2) == r2);
assert(asString(r2, ",") == "a,b,c,d,e,f");
assert(asString(r2, "[", "-", "]") == "[a-b-c-d-e-f]");
- void
wr
(R)(R range, string sep = " ");
void
wr
(R)(R range, string pre, string sep, string post);
void
wr
(R)(R range, string sep = " ");
- Small utility function to print a range. Mainly used for debugging/testing purposes.
- template
Chainable
()
- A template to mixin to get some catenation operations defined on a range, based
on std.range.chain. If r1 is a range with mixin(
Chainable
!()), r2 a range with
the same element type, and e an element, you can do:
r1 ~ r2; // Concatenation on the right.
e[] ~ r1;// Concatenation on the left with an array of elements.
r1 ~ e; // concatenation on the right with a new element.
e ~ r1; // concatenation on the left with a new element.
What you don"t get is:
r2 ~ r1; // No, r2 must define opCat. A templated opCat_r does not work if r1 and r2 both define it and opCat.
Note:
it detects Chain!R and reacts accordingly. That is, it does not build Chain!(R, Chain!(U)), but Chain!(R,U).
Note:
to be really interesting, it needs some modifications of std.range.Chain, to give it opCat capabilities.
- T[]
append
(T, E)(T[] array, E elem);
- T[]
prepend
(T, E)(T[] array, E elem);
- struct
Store
(R) if (isInputRange!(R));
- A sort-of extension of standard ranges that remembers the previous values output through
front and back and can deliver them anew. The front can go back with retreat and the back
can "go forward" with advance. Use asNew/backAsNew to see if you can still retreat/advance.
BUG:
doesn"t quite work. I"m getting fed up with this: too many internal states to consider. I should
tackle this anew.
|