| Function Name | Description |
|---|---|
| cache | Eagerly evaluates and caches another range'sfront. |
| cacheBidirectional | As above, but also providesback andpopBack. |
| chunkBy | chunkBy!((a,b) => a[1] == b[1])([[1, 1], [1, 2], [2, 2], [2, 1]]) returns a range containing 3 subranges: the first with just[1, 1]; the second with the elements[1, 2] and[2, 2]; and the third with just[2, 1]. |
| cumulativeFold | cumulativeFold!((a, b) => a + b)([1, 2, 3, 4]) returns a lazily-evaluated range containing the successive reduced values1,3,6,10. |
| each | each!writeln([1, 2, 3]) eagerly prints the numbers1,2 and3 on their own lines. |
| filter | filter!(a => a > 0)([1, -1, 2, 0, -3]) iterates over elements1 and2. |
| filterBidirectional | Similar tofilter, but also providesback andpopBack at a small increase in cost. |
| fold | fold!((a, b) => a + b)([1, 2, 3, 4]) returns10. |
| group | group([5, 2, 2, 3, 3]) returns a range containing the tuplestuple(5, 1),tuple(2, 2), andtuple(3, 2). |
| joiner | joiner(["hello", "world!"], "; ") returns a range that iterates over the characters"hello; world!". No new string is created - the existing inputs are iterated. |
| map | map!(a => a * 2)([1, 2, 3]) lazily returns a range with the numbers2,4,6. |
| mean | Colloquially known as the average,mean([1, 2, 3]) returns2. |
| permutations | Lazily computes all permutations using Heap's algorithm. |
| reduce | reduce!((a, b) => a + b)([1, 2, 3, 4]) returns10. This is the old implementation offold. |
| splitWhen | Lazily splits a range by comparing adjacent elements. |
| splitter | Lazily splits a range by a separator. |
| substitute | [1, 2].substitute(1, 0.1) returns[0.1, 2]. |
| sum | Same asfold, but specialized for accurate summation. |
| uniq | Iterates over the unique elements in a range, which is assumed sorted. |
cache(Range)(Rangerange)cacheBidirectional(Range)(Rangerange)cache eagerly evaluatesfront ofrangeon each construction or call topopFront,to store the result in a cache.The result is then directly returned whenfront is called,rather than re-evaluated.cache may providebidirectional rangeiteration if needed, but since this comes at an increased cost, it must be explicitly requested via thecall tocacheBidirectional. Furthermore, a bidirectional cache willevaluate the "center" element twice, when there is only one element left inthe range.cache does not provide random access primitives,ascache would be unable to cache the random accesses.IfRange provides slicing primitives,thencache will provide the same slicing primitives,buthasSlicing!Cache will not yield true (as thestd.range.primitives.hasSlicingtrait also checks for random access).Rangerange | aninput range |
import std.algorithm.comparison : equal;import std.range, std.stdio;import std.typecons : tuple;ulong counter = 0;double fun(int x){ ++counter;// http://en.wikipedia.org/wiki/Quartic_functionreturn ( (x + 4.0) * (x + 1.0) * (x - 1.0) * (x - 3.0) ) / 14.0 + 0.5;}// Without cache, with array (greedy)auto result1 = iota(-4, 5).map!(a =>tuple(a, fun(a)))() .filter!(a => a[1] < 0)() .map!(a => a[0])() .array();// the values of x that have a negative y are:assert(equal(result1, [-3, -2, 2]));// Check how many times fun was evaluated.// As many times as the number of items in both source and result.writeln(counter);// iota(-4, 5).length + result1.lengthcounter = 0;// Without array, with cache (lazy)auto result2 = iota(-4, 5).map!(a =>tuple(a, fun(a)))() .cache() .filter!(a => a[1] < 0)() .map!(a => a[0])();// the values of x that have a negative y are:assert(equal(result2, [-3, -2, 2]));// Check how many times fun was evaluated.// Only as many times as the number of items in source.writeln(counter);// iota(-4, 5).length
cache is eager when evaluating elements. If calling front on theunderlying range has a side effect, it will be observable before callingfront on the actual cached range.Furthermore, care should be taken composingcache withstd.range.take.By placingtake beforecache, thencache will be "aware"of when the range ends, and correctly stop caching elements when needed.If calling front has no side effect though, placingtake aftercachemay yield a faster range.Either way, the resulting ranges will be equivalent, but maybe not at thesame cost or side effects.import std.algorithm.comparison : equal;import std.range;int i = 0;auto r = iota(0, 4).tee!((a){i = a;}, No.pipeOnPop);auto r1 = r.take(3).cache();auto r2 = r.cache().take(3);assert(equal(r1, [0, 1, 2]));assert(i == 2);//The last "seen" element was 2. The data in cache has been cleared.assert(equal(r2, [0, 1, 2]));assert(i == 3);//cache has accessed 3. It is still stored internally by cache.
map(fun...) if (fun.length >= 1)map!(fun)(range)returns a range of which elements are obtained by applyingfun(a)left to right for all elementsa inrange. The original ranges arenot changed. Evaluation is done lazily.| fun | one or more transformation functions |
import std.algorithm.comparison : equal;import std.range : chain, only;auto squares = chain(only(1, 2, 3, 4), only(5, 6)).map!(a => a * a);assert(equal(squares, only(1, 4, 9, 16, 25, 36)));
map. In that case, theelement type ofmap is a tuple containing one element for eachfunction.auto sums = [2, 4, 6, 8];auto products = [1, 4, 9, 16];size_t i = 0;foreach (result; [ 1, 2, 3, 4 ].map!("a + a","a * a")){ writeln(result[0]);// sums[i] writeln(result[1]);// products[i] ++i;}
map with some function(s) to a symbol and useit separately:import std.algorithm.comparison : equal;import std.conv : to;alias stringize =map!(to!string);assert(equal(stringize([ 1, 2, 3, 4 ]), ["1","2","3","4" ]));
map(Range)(Ranger)Ranger | aninput range |
each(alias fun = "a")each defaults to doing nothing butconsuming the entire range.r.front will be evaluated, but that can be avoidedby specifying a lambda with alazy parameter.each also supportsopApply-based types, so it works with e.g.std.parallelism.parallel.Normally the entire range is iterated. If partial iteration (early stopping) isdesired,fun needs to return a value of typestd.typecons.Flag!"each" (Yes.each to continue iteration, orNo.each to stopiteration).| fun | function to apply to each element of the range |
| Range r | range or iterable over whicheach iterates |
each if the entire range was iterated,No.each in case of earlystopping.import std.range : iota;import std.typecons : No;int[] arr;iota(5).each!(n => arr ~= n);writeln(arr);// [0, 1, 2, 3, 4]// stop iterating earlyiota(5).each!((n) { arr ~= n;return No.each; });writeln(arr);// [0, 1, 2, 3, 4, 0]// If the range supports it, the value can be mutated in placearr.each!((ref n) => n++);writeln(arr);// [1, 2, 3, 4, 5, 1]arr.each!"a++";writeln(arr);// [2, 3, 4, 5, 6, 2]auto m = arr.map!(n => n);// by-ref lambdas are not allowed for non-ref rangesstaticassert(!__traits(compiles, m.each!((ref n) => n++)));// The default predicate consumes the range(&m).each();assert(m.empty);
each can pass an index variable for iterable objects which support thisauto arr =new size_t[4];arr.each!"a=i"();writeln(arr);// [0, 1, 2, 3]arr.each!((i,ref e) => e = i * 2);writeln(arr);// [0, 2, 4, 6]
staticclass S{int x;int opApply(scopeintdelegate(refint _x) dg) {return dg(x); }}auto s =new S;s.each!"a++";writeln(s.x);// 1
each"each(Range)(Ranger)r.front).length)));each"each(Iterable)(auto ref Iterabler)r.opApply))));Ranger | range or iterable over which each iterates |
filter(alias predicate) if (is(typeof(unaryFun!predicate)))filter!(predicate)(range) returns a new range containing only elementsx inrange forwhichpredicate(x) returnstrue.| predicate | Function to apply to each element of range |
filter will also be a forward range.import std.algorithm.comparison : equal;import std.math.operations : isClose;import std.range;int[] arr = [ 1, 2, 3, 4, 5 ];// Filter below 3auto small =filter!(a => a < 3)(arr);assert(equal(small, [ 1, 2 ]));// Filter again, but with Uniform Function Call Syntax (UFCS)auto sum = arr.filter!(a => a < 3);assert(equal(sum, [ 1, 2 ]));// In combination with chain() to span multiple rangesint[] a = [ 3, -2, 400 ];int[] b = [ 100, -101, 102 ];auto r = chain(a, b).filter!(a => a > 0);assert(equal(r, [ 3, 400, 100, 102 ]));// Mixing convertible types is fair game, toodouble[] c = [ 2.5, 3.0 ];auto r1 = chain(c, a, b).filter!(a =>cast(int) a != a);assert(isClose(r1, [ 2.5 ]));
filter(Range)(Rangerange)Rangerange | Aninput range of elements |
range for whichpredicate(x) returnstrue.filterBidirectional(alias pred)| pred | Function to apply to each element of range |
import std.algorithm.comparison : equal;import std.range;int[] arr = [ 1, 2, 3, 4, 5 ];auto small =filterBidirectional!("a < 3")(arr);staticassert(isBidirectionalRange!(typeof(small)));writeln(small.back);// 2assert(equal(small, [ 1, 2 ]));assert(equal(retro(small), [ 2, 1 ]));// In combination with chain() to span multiple rangesint[] a = [ 3, -2, 400 ];int[] b = [ 100, -101, 102 ];auto r =filterBidirectional!("a > 0")(chain(a, b));writeln(r.back);// 102
filterBidirectional(Range)(Ranger)Ranger | Bidirectional range of elements |
r for whichpred returnstrue.group(alias pred = "a == b", Range)(Ranger);Group(alias pred, R) if (isInputRange!R);group produces a range that iterates over uniqueconsecutive elements of the given range. Each element of this range is a tupleof the element and the number of times it is repeated in the original range.Equivalence of elements is assessed by using the predicatepred, whichdefaults to"a == b". The predicate is passed tostd.functional.binaryFun,and can either accept a string, or any callable that can be executed viapred(element, element).| pred | Binary predicate for determining equivalence of two elements. |
| R | The range type |
Ranger | Theinput range to iterate over. |
import std.algorithm.comparison : equal;import std.typecons : tuple, Tuple;int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];assert(equal(group(arr), [ tuple(1, 1u), tuple(2, 4u), tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ][]));
import std.algorithm.sorting : sort;import std.array : assocArray;uint[string] result;auto range = ["a","b","a","c","b","c","c","d","e"];result = range.sort!((a, b) => a < b) .group .assocArray;writeln(result);// ["a":2U, "b":2U, "c":3U, "d":1U, "e":1U]
chunkBy(alias pred, Range)(Ranger)| pred | Predicate for determining equivalence. |
Ranger | Aninput range to be chunked. |
NotesEquivalent elements separated by an intervening non-equivalent element will appear in separate subranges; this function only considers adjacent equivalence. Elements in the subranges will always appear in the same order they appear in the original range.
import std.algorithm.comparison : equal;// Grouping by particular attribute of each element:auto data = [ [1, 1], [1, 2], [2, 2], [2, 3]];auto r1 = data.chunkBy!((a,b) => a[0] == b[0]);assert(r1.equal!equal([ [[1, 1], [1, 2]], [[2, 2], [2, 3]]]));auto r2 = data.chunkBy!((a,b) => a[1] == b[1]);assert(r2.equal!equal([ [[1, 1]], [[1, 2], [2, 2]], [[2, 3]]]));
import std.algorithm.comparison : equal;import std.range.primitives;import std.typecons : tuple;// Grouping by particular attribute of each element:auto range =[ [1, 1], [1, 1], [1, 2], [2, 2], [2, 3], [2, 3], [3, 3]];auto byX =chunkBy!(a => a[0])(range);auto expected1 =[ tuple(1, [[1, 1], [1, 1], [1, 2]]), tuple(2, [[2, 2], [2, 3], [2, 3]]), tuple(3, [[3, 3]])];foreach (e; byX){assert(!expected1.empty); writeln(e[0]);// expected1.front[0]assert(e[1].equal(expected1.front[1])); expected1.popFront();}auto byY =chunkBy!(a => a[1])(range);auto expected2 =[ tuple(1, [[1, 1], [1, 1]]), tuple(2, [[1, 2], [2, 2]]), tuple(3, [[2, 3], [2, 3], [3, 3]])];foreach (e; byY){assert(!expected2.empty); writeln(e[0]);// expected2.front[0]assert(e[1].equal(expected2.front[1])); expected2.popFront();}
splitWhen(alias pred, Range)(Ranger)r is compared withpred to the nextelement. Ifpred return true, a new subrange is started for the next element.Otherwise, they are part of the same subrange.If the elements are compared with an inequality (!=) operator, considerchunkBy instead, as it's likely faster to execute.| pred | Predicate for determining where to split. The earlier element in thesource range is always given as the first argument. |
Ranger | Aforward range to be split. |
r, split such that within a given subrange,callingpred with any pair of adjacent elements as arguments returnsfalse.Copying the range currently has reference semantics, but this may change in the future.import std.algorithm.comparison : equal;import std.range : dropExactly;auto source = [4, 3, 2, 11, 0, -3, -3, 5, 3, 0];auto result1 = source.splitWhen!((a,b) => a <= b);assert(result1.save.equal!equal([ [4, 3, 2], [11, 0, -3], [-3], [5, 3, 0]]));//splitWhen, like chunkBy, is currently a reference range (this may change//in future). Remember to call `save` when appropriate.auto result2 = result1.dropExactly(2);assert(result1.save.equal!equal([ [-3], [5, 3, 0]]));
joiner(RoR, Separator)(RoRr, Separatorsep);joiner(RoR)(RoRr)RoRr | Aninput range of input ranges to be joined. |
Separatorsep | Aforward range of element(s) to serve as separators in the joined range. |
NoteWhen both outer and inner ranges ofRoR are bidirectional and the joiner isiterated from the back to the front, the separator will still be consumed fromfront to back, even if it is a bidirectional range too.
import std.algorithm.comparison : equal;import std.conv : text;assert(["abc","def"].joiner.equal("abcdef"));assert(["Mary","has","a","little","lamb"] .joiner("...") .equal("Mary...has...a...little...lamb"));assert(["","abc"].joiner("xyz").equal("xyzabc"));assert([""].joiner("xyz").equal(""));assert(["",""].joiner("xyz").equal("xyz"));
import std.algorithm.comparison : equal;import std.range : repeat;assert([""].joiner.equal(""));assert(["",""].joiner.equal(""));assert(["","abc"].joiner.equal("abc"));assert(["abc",""].joiner.equal("abc"));assert(["abc","def"].joiner.equal("abcdef"));assert(["Mary","has","a","little","lamb"].joiner.equal("Maryhasalittlelamb"));assert("abc".repeat(3).joiner.equal("abcabcabc"));
import std.algorithm.comparison : equal;auto a = [ [1, 2, 3], [42, 43] ];auto j =joiner(a);j.front = 44;writeln(a);// [[44, 2, 3], [42, 43]]assert(equal(j, [44, 2, 3, 42, 43]));
import std.algorithm.comparison : equal;import std.range : chain, cycle, iota, only, retro, take, zip;import std.format : format;staticimmutable number ="12345678";staticimmutable delimiter =",";auto formatted = number.retro .zip(3.iota.cycle.take(number.length)) .map!(z => chain(z[0].only, z[1] == 2 ? delimiter :null)) .joiner .retro;staticimmutable expected ="12,345,678";assert(formatted.equal(expected));
import std.algorithm.comparison : equal;import std.range : retro;auto a = [[1, 2, 3], [4, 5]];auto j = a.joiner;j.back = 44;writeln(a);// [[1, 2, 3], [4, 44]]assert(equal(j.retro, [44, 4, 3, 2, 1]));
reduce(fun...) if (fun.length >= 1)reduce!(fun)(seed, range) first assignsseed toan internal variableresult, also called the accumulator.Then, for each elementx inrange,result = fun(result, x)gets evaluated. Finally,result is returned.The one-argument versionreduce!(fun)(range)works similarly, but it uses the first element of the range as theseed (the range must be non-empty).| fun | one or more functions |
reduce!((a, b) => a + b) that offers pairwise summing of floating point numbers.reducequickly and easily. The example below illustratesreduce'sremarkable power and flexibility.import std.algorithm.comparison : max, min;import std.math.operations : isClose;import std.range;int[] arr = [ 1, 2, 3, 4, 5 ];// Sum all elementsauto sum =reduce!((a,b) => a + b)(0, arr);writeln(sum);// 15// Sum again, using a string predicate with "a" and "b"sum =reduce!"a + b"(0, arr);writeln(sum);// 15// Compute the maximum of all elementsauto largest =reduce!(max)(arr);writeln(largest);// 5// Max again, but with Uniform Function Call Syntax (UFCS)largest = arr.reduce!(max);writeln(largest);// 5// Compute the number of odd elementsauto odds =reduce!((a,b) => a + (b & 1))(0, arr);writeln(odds);// 3// Compute the sum of squaresauto ssquares =reduce!((a,b) => a + b * b)(0, arr);writeln(ssquares);// 55// Chain multiple ranges into seedint[] a = [ 3, 4 ];int[] b = [ 100 ];auto r =reduce!("a + b")(chain(a, b));writeln(r);// 107// Mixing convertible types is fair game, toodouble[] c = [ 2.5, 3.0 ];auto r1 =reduce!("a + b")(chain(a, b, c));assert(isClose(r1, 112.5));// To minimize nesting of parentheses, Uniform Function Call Syntax can be usedauto r2 = chain(a, b, c).reduce!("a + b");assert(isClose(r2, 112.5));
reduce accepts multiple functions.If two or more functions are passed,reduce returns astd.typecons.Tuple object with one member per passed-in function.The number of seeds must be correspondingly increased.import std.algorithm.comparison : max, min;import std.math.operations : isClose;import std.math.algebraic : sqrt;import std.typecons : tuple, Tuple;double[] a = [ 3.0, 4, 7, 11, 3, 2, 5 ];// Compute minimum and maximum in one passauto r =reduce!(min, max)(a);// The type of r is Tuple!(int, int)assert(isClose(r[0], 2));// minimumassert(isClose(r[1], 11));// maximum// Compute sum and sum of squares in one passr =reduce!("a + b","a + b * b")(tuple(0.0, 0.0), a);assert(isClose(r[0], 35));// sumassert(isClose(r[1], 233));// sum of squares// Compute average and standard deviation from the aboveauto avg = r[0] / a.length;writeln(avg);// 5auto stdev = sqrt(r[1] / a.length - avg * avg);writeln(cast(int)stdev);// 2
reduce(R)(Rr)r is used as the seed's value.r:ElementType!R for ranges, andForeachType!R otherwise. Once S has been determined, thenS s = e; ands = f(s, e); must both be legal.Rr | an iterable value as defined byisIterable |
r is emptyreduce(S, R)(Sseed, Rr)seed should be astd.typecons.Tuple, with one field per function inf.reduce will operate on an unqualified copy. If this happens then the returned type will not perfectly matchS. Usefold instead ofreduce to use the seed version in a UFCS chain.Sseed | the initial value of the accumulator |
Rr | an iterable value as defined byisIterable |
fold(fun...) if (fun.length >= 1)Each predicate infun must take two arguments:
Each predicate must return a value which implicitly converts to thetype of the accumulator.
For a single predicate,the callfold!(fun)(range, seed) will:
The one-argument versionfold!(fun)(range)works similarly, but it uses the first element of the range as theseed (the range must be non-empty) and iterates over the remainingelements.
| fun | the predicate function(s) to apply to the elements |
fold!((a, b) => a + b) that offers precise summing of floating point numbers.fold is functionally equivalent toreduce with the argument order reversed, and without the need to usetuple for multiple seeds.immutable arr = [1, 2, 3, 4, 5];// Sum all elementswriteln(arr.fold!((a, e) => a + e));// 15// Sum all elements with explicit seedwriteln(arr.fold!((a, e) => a + e)(6));// 21import std.algorithm.comparison : min, max;import std.typecons : tuple;// Compute minimum and maximum at the same timewriteln(arr.fold!(min, max));// tuple(1, 5)// Compute minimum and maximum at the same time with seedswriteln(arr.fold!(min, max)(0, 7));// tuple(0, 7)// Can be used in a UFCS chainwriteln(arr.map!(a => a + 1).fold!((a, e) => a + e));// 20// Return the last element of any rangewriteln(arr.fold!((a, e) => e));// 5
fold(R, S...)(Rr, Sseeds);Rr | theinput range to fold |
Sseeds | the initial values of each accumulator (optional), one for each predicate |
cumulativeFold(fun...) if (fun.length >= 1)cumulativeFold!(fun)(range, seed) first assignsseed to aninternal variableresult, also called the accumulator.The returned range contains the valuesresult = fun(result, x) lazilyevaluated for each elementx inrange. Finally, the last element has thesame value asfold!(fun)(seed, range).The one-argument versioncumulativeFold!(fun)(range) works similarly, butit returns the first element unchanged and uses it as seed for the nextelements.This function is also known aspartial_sum,accumulate,scan,Cumulative Sum.| fun | one or more functions to use as fold operation |
NoteIn functional programming languages this is typically calledscan,scanl,scanLeft orreductions.
import std.algorithm.comparison : max, min;import std.array : array;import std.math.operations : isClose;import std.range : chain;int[] arr = [1, 2, 3, 4, 5];// Partial sum of all elementsauto sum =cumulativeFold!((a, b) => a + b)(arr, 0);writeln(sum.array);// [1, 3, 6, 10, 15]// Partial sum again, using a string predicate with "a" and "b"auto sum2 =cumulativeFold!"a + b"(arr, 0);writeln(sum2.array);// [1, 3, 6, 10, 15]// Compute the partial maximum of all elementsauto largest =cumulativeFold!max(arr);writeln(largest.array);// [1, 2, 3, 4, 5]// Partial max again, but with Uniform Function Call Syntax (UFCS)largest = arr.cumulativeFold!max;writeln(largest.array);// [1, 2, 3, 4, 5]// Partial count of odd elementsauto odds = arr.cumulativeFold!((a, b) => a + (b & 1))(0);writeln(odds.array);// [1, 1, 2, 2, 3]// Compute the partial sum of squaresauto ssquares = arr.cumulativeFold!((a, b) => a + b * b)(0);writeln(ssquares.array);// [1, 5, 14, 30, 55]// Chain multiple ranges into seedint[] a = [3, 4];int[] b = [100];auto r =cumulativeFold!"a + b"(chain(a, b));writeln(r.array);// [3, 7, 107]// Mixing convertible types is fair game, toodouble[] c = [2.5, 3.0];auto r1 =cumulativeFold!"a + b"(chain(a, b, c));assert(isClose(r1, [3, 7, 107, 109.5, 112.5]));// To minimize nesting of parentheses, Uniform Function Call Syntax can be usedauto r2 = chain(a, b, c).cumulativeFold!"a + b";assert(isClose(r2, [3, 7, 107, 109.5, 112.5]));
cumulativeFold accepts multiple functions.If two or more functions are passed,cumulativeFold returns astd.typecons.Tuple object with one member per passed-in function.The number of seeds must be correspondingly increased.import std.algorithm.comparison : max, min;import std.algorithm.iteration : map;import std.math.operations : isClose;import std.typecons : tuple;double[] a = [3.0, 4, 7, 11, 3, 2, 5];// Compute minimum and maximum in one passauto r = a.cumulativeFold!(min, max);// The type of r is Tuple!(int, int)assert(isClose(r.map!"a[0]", [3, 3, 3, 3, 3, 2, 2]));// minimumassert(isClose(r.map!"a[1]", [3, 4, 7, 11, 11, 11, 11]));// maximum// Compute sum and sum of squares in one passauto r2 = a.cumulativeFold!("a + b","a + b * b")(tuple(0.0, 0.0));assert(isClose(r2.map!"a[0]", [3, 7, 14, 25, 28, 30, 35]));// sumassert(isClose(r2.map!"a[1]", [9, 25, 74, 195, 204, 208, 233]));// sum of squares
cumulativeFold(R)(Rrange)Rrange | Aninput range |
cumulativeFold(R, S)(Rrange, Sseed)seed should be astd.typecons.Tuple, with one field per function inf. For convenience, if the seed isconst, or has qualified fields, thencumulativeFold will operate on an unqualified copy. If this happens then the returned type will not perfectly matchS.Rrange | Aninput range |
Sseed | the initial value of the accumulator |
splitter(alias pred = "a == b", Flag!"keepSeparators" keepSeparators = No.keepSeparators, Range, Separator)(Ranger, Separators)r.front,s)) : bool) && (hasSlicing!Range && hasLength!Range || isNarrowString!Range));splitter(alias pred = "a == b", Flag!"keepSeparators" keepSeparators = No.keepSeparators, Range, Separator)(Ranger, Separators)r.front,s.front)) : bool) && (hasSlicing!Range || isNarrowString!Range) && isForwardRange!Separator && (hasLength!Separator || isNarrowString!Separator));splitter(alias isTerminator, Range)(Ranger)r.front))));s).NotesIf splitting a string on whitespace and token compression is desired, consider usingsplitter without specifying a separator.
r.| pred | The predicate for comparing each element with the separator, defaulting to"a == b". |
Ranger | Theinput range to be split. Must support slicing and.length or be a narrow string type. |
Separators | The element (or range) to be treated as the separator between range segments to be split. |
| isTerminator | The predicate for deciding where to split the range when no separator is passed |
| keepSeparators | The flag for deciding if the separators are kept |
ConstraintsThe predicatepred needs to accept an element ofr and the separators.
r is aforward range orbidirectional range, the returned range will be likewise. When a range is used a separator, bidirectionality isn't possible. If keepSeparators is equal to Yes.keepSeparators the output will also contain the separators. If an empty range is given, the result is an empty range. If a range with one separator is given, the result is a range with two empty elements.import std.algorithm.comparison : equal;assert("a|bc|def".splitter('|').equal(["a","bc","def" ]));int[] a = [1, 0, 2, 3, 0, 4, 5, 6];int[][] w = [ [1], [2, 3], [4, 5, 6] ];assert(a.splitter(0).equal(w));
import std.algorithm.comparison : equal;import std.typecons : Yes;assert("a|bc|def".splitter!("a == b", Yes.keepSeparators)('|') .equal(["a","|","bc","|","def" ]));int[] a = [1, 0, 2, 3, 0, 4, 5, 6];int[][] w = [ [1], [0], [2, 3], [0], [4, 5, 6] ];assert(a.splitter!("a == b", Yes.keepSeparators)(0).equal(w));
import std.algorithm.comparison : equal;assert("|ab|".splitter('|').equal(["","ab","" ]));assert("ab".splitter('|').equal(["ab" ]));assert("a|b||c".splitter('|').equal(["a","b","","c" ]));assert("hello world".splitter(' ').equal(["hello","","world" ]));auto a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];auto w = [ [1, 2], [], [3], [4, 5], [] ];assert(a.splitter(0).equal(w));
import std.algorithm.comparison : equal;import std.typecons : Yes;assert("|ab|".splitter!("a == b", Yes.keepSeparators)('|') .equal(["","|","ab","|","" ]));assert("ab".splitter!("a == b", Yes.keepSeparators)('|') .equal(["ab" ]));assert("a|b||c".splitter!("a == b", Yes.keepSeparators)('|') .equal(["a","|","b","|","","|","c" ]));assert("hello world".splitter!("a == b", Yes.keepSeparators)(' ') .equal(["hello"," ",""," ","world" ]));auto a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];auto w = [ [1, 2], [0], [], [0], [3], [0], [4, 5], [0], [] ];assert(a.splitter!("a == b", Yes.keepSeparators)(0).equal(w));
import std.algorithm.comparison : equal;import std.range : empty;assert("".splitter('|').empty);assert("|".splitter('|').equal(["","" ]));assert("||".splitter('|').equal(["","","" ]));
import std.algorithm.comparison : equal;import std.typecons : Yes;import std.range : empty;assert("".splitter!("a == b", Yes.keepSeparators)('|').empty);assert("|".splitter!("a == b", Yes.keepSeparators)('|') .equal(["","|","" ]));assert("||".splitter!("a == b", Yes.keepSeparators)('|') .equal(["","|","","|","" ]));
import std.algorithm.comparison : equal;assert("a=>bc=>def".splitter("=>").equal(["a","bc","def" ]));assert("a|b||c".splitter("||").equal(["a|b","c" ]));assert("hello world".splitter(" ").equal(["hello","world" ]));int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];int[][] w = [ [1, 2], [3, 0, 4, 5, 0] ];assert(a.splitter([0, 0]).equal(w));a = [ 0, 0 ];assert(a.splitter([0, 0]).equal([ (int[]).init, (int[]).init ]));a = [ 0, 0, 1 ];assert(a.splitter([0, 0]).equal([ [], [1] ]));
import std.algorithm.comparison : equal;import std.typecons : Yes;assert("a=>bc=>def".splitter!("a == b", Yes.keepSeparators)("=>") .equal(["a","=>","bc","=>","def" ]));assert("a|b||c".splitter!("a == b", Yes.keepSeparators)("||") .equal(["a|b","||","c" ]));assert("hello world".splitter!("a == b", Yes.keepSeparators)(" ") .equal(["hello"," ","world" ]));int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];int[][] w = [ [1, 2], [0, 0], [3, 0, 4, 5, 0] ];assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0]).equal(w));a = [ 0, 0 ];assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0]) .equal([ (int[]).init, [0, 0], (int[]).init ]));a = [ 0, 0, 1 ];assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0]) .equal([ [], [0, 0], [1] ]));
import std.algorithm.comparison : equal;import std.ascii : toLower;assert("abXcdxef".splitter!"a.toLower == b"('x').equal( ["ab","cd","ef" ]));auto w = [ [0], [1], [2] ];assert(w.splitter!"a.front == b"(1).equal([ [[0]], [[2]] ]));
import std.algorithm.comparison : equal;import std.typecons : Yes;import std.ascii : toLower;assert("abXcdxef".splitter!("a.toLower == b", Yes.keepSeparators)('x') .equal(["ab","X","cd","x","ef" ]));auto w = [ [0], [1], [2] ];assert(w.splitter!("a.front == b", Yes.keepSeparators)(1) .equal([ [[0]], [[1]], [[2]] ]));
import std.algorithm.comparison : equal;import std.range.primitives : front;assert(equal(splitter!(a => a == '|')("a|bc|def"), ["a","bc","def" ]));assert(equal(splitter!(a => a == ' ')("hello world"), ["hello","","world" ]));int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];int[][] w = [ [1, 2], [], [3], [4, 5], [] ];assert(equal(splitter!(a => a == 0)(a), w));a = [ 0 ];assert(equal(splitter!(a => a == 0)(a), [ (int[]).init, (int[]).init ]));a = [ 0, 1 ];assert(equal(splitter!(a => a == 0)(a), [ [], [1] ]));w = [ [0], [1], [2] ];assert(equal(splitter!(a => a.front == 1)(w), [ [[0]], [[2]] ]));
import std.algorithm.comparison : equal;assert("|ab|".splitter('|').equal(["","ab","" ]));assert("ab".splitter('|').equal(["ab" ]));
import std.algorithm.comparison : equal;import std.typecons : Yes;assert("|ab|".splitter!("a == b", Yes.keepSeparators)('|') .equal(["","|","ab","|","" ]));assert("ab".splitter!("a == b", Yes.keepSeparators)('|') .equal(["ab" ]));
import std.algorithm.comparison : equal;import std.range : retro;assert("a|bc|def".splitter('|').retro.equal(["def","bc","a" ]));
import std.algorithm.comparison : equal;import std.typecons : Yes;import std.range : retro;assert("a|bc|def".splitter!("a == b", Yes.keepSeparators)('|') .retro.equal(["def","|","bc","|","a" ]));
import std.ascii : isWhite;import std.algorithm.comparison : equal;import std.algorithm.iteration :splitter;string str ="Hello World!";assert(str.splitter!(isWhite).equal(["Hello","World!"]));
splitter(Range)(Ranges)s into words, using whitespace as thedelimiter.splitter!(std.uni.isWhite), runs of whitespace will be merged together(no empty tokens will be produced).Ranges | The character-based range to be split. Must be a string, or a random-access range of character types. |
import std.algorithm.comparison : equal;auto a =" a bcd ef gh ";assert(equal(splitter(a), ["a","bcd","ef","gh"][]));
substitute(substs...) if (substs.length >= 2 && isExpressions!substs)substitute(alias pred = (a, b) => a == b, R, Substs...)(Rr, Substssubsts)substs inr.replaced with their substitution.substitute!('ä', 'a', 'ö', 'o', 'ü', 'u)) aresupported as well and inΟ(1).Rr | aninput range |
| Value value | a single value which can be substituted inΟ(1) |
Substssubsts | a set of replacements/substitutions |
| pred | the equality function to test if element(s) are equal to a substitution |
import std.algorithm.comparison : equal;// substitute single elementsassert("do_it".substitute('_', ' ').equal("do it"));// substitute multiple, single elementsassert("do_it".substitute('_', ' ', 'd', 'g', 'i', 't', 't', 'o') .equal("go to"));// substitute subrangesassert("do_it".substitute("_"," ","do","done") .equal("done it"));// substitution works for any ElementTypeint[] x = [1, 2, 3];auto y = x.substitute(1, 0.1);assert(y.equal([0.1, 2, 3]));staticassert(is(typeof(y.front) ==double));import std.range : retro;assert([1, 2, 3].substitute(1, 0.1).retro.equal([3, 2, 0.1]));
import std.algorithm.comparison : equal;// substitute subranges of a rangeassert("apple_tree".substitute!("apple","banana","tree","shrub").equal("banana_shrub"));// substitute subranges of a rangeassert("apple_tree".substitute!('a', 'b', 't', 'f').equal("bpple_free"));// substitute valueswriteln('a'.substitute!('a', 'b', 't', 'f'));// 'b'
import std.algorithm.comparison : equal;import std.range.primitives : ElementType;int[3] x = [1, 2, 3];auto y = x[].substitute(1, 0.1) .substitute(0.1, 0.2);staticassert(is(typeof(y.front) ==double));assert(y.equal([0.2, 2, 3]));auto z ="42".substitute('2', '3') .substitute('3', '1');staticassert(is(ElementType!(typeof(z)) ==dchar));assert(equal(z,"41"));
substitute(Value)(Valuevalue)ComplexityΟ(1) due to D'sswitch guaranteeingΟ(1);
sum(R)(Rr)r.front +r.front)));sum(R, E)(Rr, Eseed)seed =seed +r.front)));r, which must be a finiteinput range. Althoughconceptuallysum(r) is equivalent tofold!((a, b) => a +b)(r, 0),sum uses specialized algorithms to maximize accuracy,as follows.sum uses thepairwise summationalgorithm.sum uses theKahan summation algorithm.sum. Not only will this seed be used as an initialvalue, but its type will override all the above, and determine the algorithmand precision used for summation. If a seed is not passed, one is created withthe value oftypeof(r.front +r.front)(0), ortypeof(r.front +r.front).zeroif no constructor exists that takes an int.Note that these specialized summing algorithms execute more primitive operationsthan vanilla summation. Therefore, if in certain cases maximum speed is requiredat expense of precision, one can usefold!((a, b) => a + b)(r, 0), whichis not specialized for summation.Eseed | the initial value of the summation |
Rr | a finite input range |
import std.range;//simple integral sumationwriteln(sum([1, 2, 3, 4]));// 10//with integral promotionwriteln(sum([false,true,true,false,true]));// 3writeln(sum(ubyte.max.repeat(100)));// 25500//The result may overflowwriteln(uint.max.repeat(3).sum());// 4294967293U//But a seed can be used to change the sumation primitivewriteln(uint.max.repeat(3).sum(ulong.init));// 12884901885UL//Floating point sumationwriteln(sum([1.0, 2.0, 3.0, 4.0]));// 10//Floating point operations have double precision minimumstaticassert(is(typeof(sum([1F, 2F, 3F, 4F])) ==double));writeln(sum([1F, 2, 3, 4]));// 10//Force pair-wise floating point sumation on large integersimport std.math.operations : isClose;assert(iota(ulong.max / 2,ulong.max / 2 + 4096).sum(0.0) .isClose((ulong.max / 2) * 4096.0 + 4096^^2 / 2));
mean(T = double, R)(Rr)mean(R, T)(Rr, Tseed)r.front +seed)) && is(typeof(r.front / size_t(1))) && !isInfinite!R);seed is needed in order to correctlyseed the summation with the equivalent to0.The first overload of this function will returnT.init if the rangeis empty. However, the second overload will returnseed on empty ranges.This function isΟ(r.length).| T | The type of the return value. |
Rr | Aninput range |
Tseed | For user defined types. Should be equivalent to0. |
r whenr is non-empty.import std.math.operations : isClose;import std.math.traits : isNaN;staticimmutable arr1 = [1, 2, 3];staticimmutable arr2 = [1.5, 2.5, 12.5];assert(arr1.mean.isClose(2));assert(arr2.mean.isClose(5.5));assert(arr1[0 .. 0].mean.isNaN);
uniq(alias pred = "a == b", Range)(Ranger)r.front,r.front)) == bool));uniq also yields abidirectional range.| pred | Predicate for determining equivalence between range elements. |
Ranger | Aninput range of elements to filter. |
r is also a forward range or bidirectional range, the returned range will be likewise.import std.algorithm.comparison : equal;import std.algorithm.mutation : copy;int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];assert(equal(uniq(arr), [ 1, 2, 3, 4, 5 ][]));// Filter duplicates in-place using copyarr.length -= arr.uniq().copy(arr).length;writeln(arr);// [1, 2, 3, 4, 5]// Note that uniqueness is only determined consecutively; duplicated// elements separated by an intervening different element will not be// eliminated:assert(equal(uniq([ 1, 1, 2, 1, 1, 3, 1]), [1, 2, 1, 3, 1]));
permutations(Range)(Ranger);Permutations(Range);r usingHeap's algorithm.| Range | the range type |
Ranger | therandom access range to find the permutations for. |
import std.algorithm.comparison : equal;import std.range : iota;assert(equal!equal(iota(3).permutations, [[0, 1, 2], [1, 0, 2], [2, 0, 1], [0, 2, 1], [1, 2, 0], [2, 1, 0]]));