1 // Written in the D programming language.
2 /**
3 This is a submodule of $(MREF std, algorithm).
4 It contains generic _iteration algorithms.
5
6 $(SCRIPT inhibitQuickIndex = 1;)
7 $(BOOKTABLE Cheat Sheet,
8 $(TR $(TH Function Name) $(TH Description))
9 $(T2 cache,
10 Eagerly evaluates and caches another range's $(D front).)
11 $(T2 cacheBidirectional,
12 As above, but also provides $(D back) and $(D popBack).)
13 $(T2 chunkBy,
14 $(D chunkBy!((a,b) => a[1] == b[1])([[1, 1], [1, 2], [2, 2], [2, 1]]))
15 returns a range containing 3 subranges: the first with just
16 $(D [1, 1]); the second with the elements $(D [1, 2]) and $(D [2, 2]);
17 and the third with just $(D [2, 1]).)
18 $(T2 cumulativeFold,
19 $(D cumulativeFold!((a, b) => a + b)([1, 2, 3, 4])) returns a
20 lazily-evaluated range containing the successive reduced values `1`,
21 `3`, `6`, `10`.)
22 $(T2 each,
23 $(D each!writeln([1, 2, 3])) eagerly prints the numbers $(D 1), $(D 2)
24 and $(D 3) on their own lines.)
25 $(T2 filter,
26 $(D filter!(a => a > 0)([1, -1, 2, 0, -3])) iterates over elements $(D 1)
27 and $(D 2).)
28 $(T2 filterBidirectional,
29 Similar to $(D filter), but also provides $(D back) and $(D popBack) at
30 a small increase in cost.)
31 $(T2 fold,
32 $(D fold!((a, b) => a + b)([1, 2, 3, 4])) returns $(D 10).)
33 $(T2 group,
34 $(D group([5, 2, 2, 3, 3])) returns a range containing the tuples
35 $(D tuple(5, 1)), $(D tuple(2, 2)), and $(D tuple(3, 2)).)
36 $(T2 joiner,
37 $(D joiner(["hello", "world!"], "; ")) returns a range that iterates
38 over the characters $(D "hello; world!"). No new string is created -
39 the existing inputs are iterated.)
40 $(T2 map,
41 $(D map!(a => a * 2)([1, 2, 3])) lazily returns a range with the numbers
42 $(D 2), $(D 4), $(D 6).)
43 $(T2 permutations,
44 Lazily computes all permutations using Heap's algorithm.)
45 $(T2 reduce,
46 $(D reduce!((a, b) => a + b)([1, 2, 3, 4])) returns $(D 10).
47 This is the old implementation of `fold`.)
48 $(T2 splitter,
49 Lazily splits a range by a separator.)
50 $(T2 sum,
51 Same as $(D fold), but specialized for accurate summation.)
52 $(T2 uniq,
53 Iterates over the unique elements in a range, which is assumed sorted.)
54 )
55
56 Copyright: Andrei Alexandrescu 2008-.
57
58 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
59
60 Authors: $(HTTP erdani.com, Andrei Alexandrescu)
61
62 Source: $(PHOBOSSRC std/algorithm/_iteration.d)
63
64 Macros:
65 T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
66 */
67 module std.algorithm.iteration;
68
69 // FIXME
70 import std.functional; // : unaryFun, binaryFun;
71 import std.range.primitives;
72 import std.traits;
73
74 private template aggregate(fun...)
75 if (fun.length >= 1)
76 {
77 /* --Intentionally not ddoc--
78 * Aggregates elements in each subrange of the given range of ranges using
79 * the given aggregating function(s).
80 * Params:
81 * fun = One or more aggregating functions (binary functions that return a
82 * single _aggregate value of their arguments).
83 * ror = A range of ranges to be aggregated.
84 *
85 * Returns:
86 * A range representing the aggregated value(s) of each subrange
87 * of the original range. If only one aggregating function is specified,
88 * each element will be the aggregated value itself; if multiple functions
89 * are specified, each element will be a tuple of the aggregated values of
90 * each respective function.
91 */
92 auto aggregate(RoR)(RoR ror)
93 if (isInputRange!RoR && isIterable!(ElementType!RoR))
94 {
95 return ror.map!(reduce!fun);
96 }
97
98 @safe unittest
99 {
100 import std.algorithm.comparison : equal, max, min;
101
102 auto data = [[4, 2, 1, 3], [4, 9, -1, 3, 2], [3]];
103
104 // Single aggregating function
105 auto agg1 = data.aggregate!max;
106 assert(agg1.equal([4, 9, 3]));
107
108 // Multiple aggregating functions
109 import std.typecons : tuple;
110 auto agg2 = data.aggregate!(max, min);
111 assert(agg2.equal([
112 tuple(4, 1),
113 tuple(9, -1),
114 tuple(3, 3)
115 ]));
116 }
117 }
118
119 /++
120 $(D cache) eagerly evaluates $(D front) of $(D range)
121 on each construction or call to $(D popFront),
122 to store the result in a cache.
123 The result is then directly returned when $(D front) is called,
124 rather than re-evaluated.
125
126 This can be a useful function to place in a chain, after functions
127 that have expensive evaluation, as a lazy alternative to $(REF array, std,array).
128 In particular, it can be placed after a call to $(D map), or before a call
129 to $(D filter).
130
131 $(D cache) may provide
132 $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives)
133 iteration if needed, but since this comes at an increased cost, it must be explicitly requested via the
134 call to $(D cacheBidirectional). Furthermore, a bidirectional cache will
135 evaluate the "center" element twice, when there is only one element left in
136 the range.
137
138 $(D cache) does not provide random access primitives,
139 as $(D cache) would be unable to cache the random accesses.
140 If $(D Range) provides slicing primitives,
141 then $(D cache) will provide the same slicing primitives,
142 but $(D hasSlicing!Cache) will not yield true (as the $(REF hasSlicing, std,_range,primitives)
143 trait also checks for random access).
144
145 Params:
146 range = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
147
148 Returns:
149 an input range with the cached values of range
150 +/
151 auto cache(Range)(Range range)
152 if (isInputRange!Range)
153 {
154 return _Cache!(Range, false)(range);
155 }
156
157 /// ditto
158 auto cacheBidirectional(Range)(Range range)
159 if (isBidirectionalRange!Range)
160 {
161 return _Cache!(Range, true)(range);
162 }
163
164 ///
165 @safe unittest
166 {
167 import std.algorithm.comparison : equal;
168 import std.range, std.stdio;
169 import std.typecons : tuple;
170
171 ulong counter = 0;
fun(int x)172 double fun(int x)
173 {
174 ++counter;
175 // http://en.wikipedia.org/wiki/Quartic_function
176 return ( (x + 4.0) * (x + 1.0) * (x - 1.0) * (x - 3.0) ) / 14.0 + 0.5;
177 }
178 // Without cache, with array (greedy)
179 auto result1 = iota(-4, 5).map!(a =>tuple(a, fun(a)))()
180 .filter!(a => a[1] < 0)()
181 .map!(a => a[0])()
182 .array();
183
184 // the values of x that have a negative y are:
185 assert(equal(result1, [-3, -2, 2]));
186
187 // Check how many times fun was evaluated.
188 // As many times as the number of items in both source and result.
189 assert(counter == iota(-4, 5).length + result1.length);
190
191 counter = 0;
192 // Without array, with cache (lazy)
193 auto result2 = iota(-4, 5).map!(a =>tuple(a, fun(a)))()
194 .cache()
195 .filter!(a => a[1] < 0)()
196 .map!(a => a[0])();
197
198 // the values of x that have a negative y are:
199 assert(equal(result2, [-3, -2, 2]));
200
201 // Check how many times fun was evaluated.
202 // Only as many times as the number of items in source.
203 assert(counter == iota(-4, 5).length);
204 }
205
206 /++
207 Tip: $(D cache) is eager when evaluating elements. If calling front on the
208 underlying _range has a side effect, it will be observable before calling
209 front on the actual cached _range.
210
211 Furthermore, care should be taken composing $(D cache) with $(REF take, std,_range).
212 By placing $(D take) before $(D cache), then $(D cache) will be "aware"
213 of when the _range ends, and correctly stop caching elements when needed.
214 If calling front has no side effect though, placing $(D take) after $(D cache)
215 may yield a faster _range.
216
217 Either way, the resulting ranges will be equivalent, but maybe not at the
218 same cost or side effects.
219 +/
220 @safe unittest
221 {
222 import std.algorithm.comparison : equal;
223 import std.range;
224 int i = 0;
225
226 auto r = iota(0, 4).tee!((a){i = a;}, No.pipeOnPop);
227 auto r1 = r.take(3).cache();
228 auto r2 = r.cache().take(3);
229
230 assert(equal(r1, [0, 1, 2]));
231 assert(i == 2); //The last "seen" element was 2. The data in cache has been cleared.
232
233 assert(equal(r2, [0, 1, 2]));
234 assert(i == 3); //cache has accessed 3. It is still stored internally by cache.
235 }
236
237 @safe unittest
238 {
239 import std.algorithm.comparison : equal;
240 import std.range;
241 auto a = [1, 2, 3, 4];
242 assert(equal(a.map!(a => (a - 1) * a)().cache(), [ 0, 2, 6, 12]));
243 assert(equal(a.map!(a => (a - 1) * a)().cacheBidirectional().retro(), [12, 6, 2, 0]));
244 auto r1 = [1, 2, 3, 4].cache() [1 .. $];
245 auto r2 = [1, 2, 3, 4].cacheBidirectional()[1 .. $];
246 assert(equal(r1, [2, 3, 4]));
247 assert(equal(r2, [2, 3, 4]));
248 }
249
250 @safe unittest
251 {
252 import std.algorithm.comparison : equal;
253
254 //immutable test
255 static struct S
256 {
257 int i;
thisS258 this(int i)
259 {
260 //this.i = i;
261 }
262 }
263 immutable(S)[] s = [S(1), S(2), S(3)];
264 assert(equal(s.cache(), s));
265 assert(equal(s.cacheBidirectional(), s));
266 }
267
268 @safe pure nothrow unittest
269 {
270 import std.algorithm.comparison : equal;
271
272 //safety etc
273 auto a = [1, 2, 3, 4];
274 assert(equal(a.cache(), a));
275 assert(equal(a.cacheBidirectional(), a));
276 }
277
278 @safe unittest
279 {
280 char[][] stringbufs = ["hello".dup, "world".dup];
281 auto strings = stringbufs.map!((a)=>a.idup)().cache();
282 assert(strings.front is strings.front);
283 }
284
285 @safe unittest
286 {
287 import std.range : cycle;
288 import std.algorithm.comparison : equal;
289
290 auto c = [1, 2, 3].cycle().cache();
291 c = c[1 .. $];
292 auto d = c[0 .. 1];
293 assert(d.equal([2]));
294 }
295
296 @safe unittest
297 {
298 static struct Range
299 {
300 bool initialized = false;
frontRange301 bool front() @property {return initialized = true;}
popFrontRange302 void popFront() {initialized = false;}
303 enum empty = false;
304 }
305 auto r = Range().cache();
306 assert(r.source.initialized == true);
307 }
308
_Cache(R,bool bidir)309 private struct _Cache(R, bool bidir)
310 {
311 import core.exception : RangeError;
312
313 private
314 {
315 import std.algorithm.internal : algoFormat;
316 import std.meta : AliasSeq;
317
318 alias E = ElementType!R;
319 alias UE = Unqual!E;
320
321 R source;
322
323 static if (bidir) alias CacheTypes = AliasSeq!(UE, UE);
324 else alias CacheTypes = AliasSeq!UE;
325 CacheTypes caches;
326
327 static assert(isAssignable!(UE, E) && is(UE : E),
328 algoFormat(
329 "Cannot instantiate range with %s because %s elements are not assignable to %s.",
330 R.stringof,
331 E.stringof,
332 UE.stringof
333 )
334 );
335 }
336
337 this(R range)
338 {
339 source = range;
340 if (!range.empty)
341 {
342 caches[0] = source.front;
343 static if (bidir)
344 caches[1] = source.back;
345 }
346 }
347
348 static if (isInfinite!R)
349 enum empty = false;
350 else
351 bool empty() @property
352 {
353 return source.empty;
354 }
355
356 static if (hasLength!R) auto length() @property
357 {
358 return source.length;
359 }
360
361 E front() @property
362 {
363 version (assert) if (empty) throw new RangeError();
364 return caches[0];
365 }
366 static if (bidir) E back() @property
367 {
368 version (assert) if (empty) throw new RangeError();
369 return caches[1];
370 }
371
372 void popFront()
373 {
374 version (assert) if (empty) throw new RangeError();
375 source.popFront();
376 if (!source.empty)
377 caches[0] = source.front;
378 else
379 caches = CacheTypes.init;
380 }
381 static if (bidir) void popBack()
382 {
383 version (assert) if (empty) throw new RangeError();
384 source.popBack();
385 if (!source.empty)
386 caches[1] = source.back;
387 else
388 caches = CacheTypes.init;
389 }
390
391 static if (isForwardRange!R)
392 {
393 private this(R source, ref CacheTypes caches)
394 {
395 this.source = source;
396 this.caches = caches;
397 }
398 typeof(this) save() @property
399 {
400 return typeof(this)(source.save, caches);
401 }
402 }
403
404 static if (hasSlicing!R)
405 {
406 enum hasEndSlicing = is(typeof(source[size_t.max .. $]));
407
408 static if (hasEndSlicing)
409 {
410 private static struct DollarToken{}
411 enum opDollar = DollarToken.init;
412
413 auto opSlice(size_t low, DollarToken)
414 {
415 return typeof(this)(source[low .. $]);
416 }
417 }
418
419 static if (!isInfinite!R)
420 {
421 typeof(this) opSlice(size_t low, size_t high)
422 {
423 return typeof(this)(source[low .. high]);
424 }
425 }
426 else static if (hasEndSlicing)
427 {
428 auto opSlice(size_t low, size_t high)
429 in
430 {
431 assert(low <= high, "Bounds error when slicing cache.");
432 }
433 body
434 {
435 import std.range : takeExactly;
436 return this[low .. $].takeExactly(high - low);
437 }
438 }
439 }
440 }
441
442 /**
443 $(D auto map(Range)(Range r) if (isInputRange!(Unqual!Range));)
444
445 Implements the homonym function (also known as $(D transform)) present
446 in many languages of functional flavor. The call $(D map!(fun)(range))
447 returns a range of which elements are obtained by applying $(D fun(a))
448 left to right for all elements $(D a) in $(D range). The original ranges are
449 not changed. Evaluation is done lazily.
450
451 Params:
452 fun = one or more transformation functions
453 r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
454
455 Returns:
456 a range with each fun applied to all the elements. If there is more than one
457 fun, the element type will be $(D Tuple) containing one element for each fun.
458
459 See_Also:
460 $(HTTP en.wikipedia.org/wiki/Map_(higher-order_function), Map (higher-order function))
461 */
462 template map(fun...)
463 if (fun.length >= 1)
464 {
465 auto map(Range)(Range r) if (isInputRange!(Unqual!Range))
466 {
467 import std.meta : AliasSeq, staticMap;
468
469 alias RE = ElementType!(Range);
470 static if (fun.length > 1)
471 {
472 import std.functional : adjoin;
473 import std.meta : staticIndexOf;
474
475 alias _funs = staticMap!(unaryFun, fun);
476 alias _fun = adjoin!_funs;
477
478 // Once DMD issue #5710 is fixed, this validation loop can be moved into a template.
foreach(f;_funs)479 foreach (f; _funs)
480 {
481 static assert(!is(typeof(f(RE.init)) == void),
482 "Mapping function(s) must not return void: " ~ _funs.stringof);
483 }
484 }
485 else
486 {
487 alias _fun = unaryFun!fun;
488 alias _funs = AliasSeq!(_fun);
489
490 // Do the validation separately for single parameters due to DMD issue #15777.
491 static assert(!is(typeof(_fun(RE.init)) == void),
492 "Mapping function(s) must not return void: " ~ _funs.stringof);
493 }
494
495 return MapResult!(_fun, Range)(r);
496 }
497 }
498
499 ///
500 @safe unittest
501 {
502 import std.algorithm.comparison : equal;
503 import std.range : chain;
504 int[] arr1 = [ 1, 2, 3, 4 ];
505 int[] arr2 = [ 5, 6 ];
506 auto squares = map!(a => a * a)(chain(arr1, arr2));
507 assert(equal(squares, [ 1, 4, 9, 16, 25, 36 ]));
508 }
509
510 /**
511 Multiple functions can be passed to $(D map). In that case, the
512 element type of $(D map) is a tuple containing one element for each
513 function.
514 */
515 @safe unittest
516 {
517 auto sums = [2, 4, 6, 8];
518 auto products = [1, 4, 9, 16];
519
520 size_t i = 0;
521 foreach (result; [ 1, 2, 3, 4 ].map!("a + a", "a * a"))
522 {
523 assert(result[0] == sums[i]);
524 assert(result[1] == products[i]);
525 ++i;
526 }
527 }
528
529 /**
530 You may alias $(D map) with some function(s) to a symbol and use
531 it separately:
532 */
533 @safe unittest
534 {
535 import std.algorithm.comparison : equal;
536 import std.conv : to;
537
538 alias stringize = map!(to!string);
539 assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ]));
540 }
541
542 @safe unittest
543 {
544 // Verify workaround for DMD #15777
545
546 import std.algorithm.mutation, std.string;
foo(string[]args)547 auto foo(string[] args)
548 {
549 return args.map!strip;
550 }
551 }
552
MapResult(alias fun,Range)553 private struct MapResult(alias fun, Range)
554 {
555 alias R = Unqual!Range;
556 R _input;
557
558 static if (isBidirectionalRange!R)
559 {
560 @property auto ref back()()
561 {
562 assert(!empty, "Attempting to fetch the back of an empty map.");
563 return fun(_input.back);
564 }
565
566 void popBack()()
567 {
568 assert(!empty, "Attempting to popBack an empty map.");
569 _input.popBack();
570 }
571 }
572
573 this(R input)
574 {
575 _input = input;
576 }
577
578 static if (isInfinite!R)
579 {
580 // Propagate infinite-ness.
581 enum bool empty = false;
582 }
583 else
584 {
585 @property bool empty()
586 {
587 return _input.empty;
588 }
589 }
590
591 void popFront()
592 {
593 assert(!empty, "Attempting to popFront an empty map.");
594 _input.popFront();
595 }
596
597 @property auto ref front()
598 {
599 assert(!empty, "Attempting to fetch the front of an empty map.");
600 return fun(_input.front);
601 }
602
603 static if (isRandomAccessRange!R)
604 {
605 static if (is(typeof(_input[ulong.max])))
606 private alias opIndex_t = ulong;
607 else
608 private alias opIndex_t = uint;
609
610 auto ref opIndex(opIndex_t index)
611 {
612 return fun(_input[index]);
613 }
614 }
615
616 static if (hasLength!R)
617 {
618 @property auto length()
619 {
620 return _input.length;
621 }
622
623 alias opDollar = length;
624 }
625
626 static if (hasSlicing!R)
627 {
628 static if (is(typeof(_input[ulong.max .. ulong.max])))
629 private alias opSlice_t = ulong;
630 else
631 private alias opSlice_t = uint;
632
633 static if (hasLength!R)
634 {
635 auto opSlice(opSlice_t low, opSlice_t high)
636 {
637 return typeof(this)(_input[low .. high]);
638 }
639 }
640 else static if (is(typeof(_input[opSlice_t.max .. $])))
641 {
642 struct DollarToken{}
643 enum opDollar = DollarToken.init;
644 auto opSlice(opSlice_t low, DollarToken)
645 {
646 return typeof(this)(_input[low .. $]);
647 }
648
649 auto opSlice(opSlice_t low, opSlice_t high)
650 {
651 import std.range : takeExactly;
652 return this[low .. $].takeExactly(high - low);
653 }
654 }
655 }
656
657 static if (isForwardRange!R)
658 {
659 @property auto save()
660 {
661 return typeof(this)(_input.save);
662 }
663 }
664 }
665
666 @safe unittest
667 {
668 import std.algorithm.comparison : equal;
669 import std.conv : to;
670 import std.functional : adjoin;
671
672 alias stringize = map!(to!string);
673 assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ]));
674
675 uint counter;
676 alias count = map!((a) { return counter++; });
677 assert(equal(count([ 10, 2, 30, 4 ]), [ 0, 1, 2, 3 ]));
678
679 counter = 0;
680 adjoin!((a) { return counter++; }, (a) { return counter++; })(1);
681 alias countAndSquare = map!((a) { return counter++; }, (a) { return counter++; });
682 //assert(equal(countAndSquare([ 10, 2 ]), [ tuple(0u, 100), tuple(1u, 4) ]));
683 }
684
685 @safe unittest
686 {
687 import std.algorithm.comparison : equal;
688 import std.ascii : toUpper;
689 import std.internal.test.dummyrange;
690 import std.range;
691 import std.typecons : tuple;
692 import std.random : unpredictableSeed, uniform, Random;
693
694 int[] arr1 = [ 1, 2, 3, 4 ];
695 const int[] arr1Const = arr1;
696 int[] arr2 = [ 5, 6 ];
697 auto squares = map!("a * a")(arr1Const);
698 assert(squares[$ - 1] == 16);
699 assert(equal(squares, [ 1, 4, 9, 16 ][]));
700 assert(equal(map!("a * a")(chain(arr1, arr2)), [ 1, 4, 9, 16, 25, 36 ][]));
701
702 // Test the caching stuff.
703 assert(squares.back == 16);
704 auto squares2 = squares.save;
705 assert(squares2.back == 16);
706
707 assert(squares2.front == 1);
708 squares2.popFront();
709 assert(squares2.front == 4);
710 squares2.popBack();
711 assert(squares2.front == 4);
712 assert(squares2.back == 9);
713
714 assert(equal(map!("a * a")(chain(arr1, arr2)), [ 1, 4, 9, 16, 25, 36 ][]));
715
716 uint i;
717 foreach (e; map!("a", "a * a")(arr1))
718 {
719 assert(e[0] == ++i);
720 assert(e[1] == i * i);
721 }
722
723 // Test length.
724 assert(squares.length == 4);
725 assert(map!"a * a"(chain(arr1, arr2)).length == 6);
726
727 // Test indexing.
728 assert(squares[0] == 1);
729 assert(squares[1] == 4);
730 assert(squares[2] == 9);
731 assert(squares[3] == 16);
732
733 // Test slicing.
734 auto squareSlice = squares[1 .. squares.length - 1];
735 assert(equal(squareSlice, [4, 9][]));
736 assert(squareSlice.back == 9);
737 assert(squareSlice[1] == 9);
738
739 // Test on a forward range to make sure it compiles when all the fancy
740 // stuff is disabled.
741 auto fibsSquares = map!"a * a"(recurrence!("a[n-1] + a[n-2]")(1, 1));
742 assert(fibsSquares.front == 1);
743 fibsSquares.popFront();
744 fibsSquares.popFront();
745 assert(fibsSquares.front == 4);
746 fibsSquares.popFront();
747 assert(fibsSquares.front == 9);
748
749 auto repeatMap = map!"a"(repeat(1));
750 auto gen = Random(unpredictableSeed);
751 auto index = uniform(0, 1024, gen);
752 static assert(isInfinite!(typeof(repeatMap)));
753 assert(repeatMap[index] == 1);
754
755 auto intRange = map!"a"([1,2,3]);
756 static assert(isRandomAccessRange!(typeof(intRange)));
757 assert(equal(intRange, [1, 2, 3]));
758
foreach(DummyType;AllDummyRanges)759 foreach (DummyType; AllDummyRanges)
760 {
761 DummyType d;
762 auto m = map!"a * a"(d);
763
764 static assert(propagatesRangeType!(typeof(m), DummyType));
765 assert(equal(m, [1,4,9,16,25,36,49,64,81,100]));
766 }
767
768 //Test string access
769 string s1 = "hello world!";
770 dstring s2 = "日本語";
771 dstring s3 = "hello world!"d;
772 auto ms1 = map!(std.ascii.toUpper)(s1);
773 auto ms2 = map!(std.ascii.toUpper)(s2);
774 auto ms3 = map!(std.ascii.toUpper)(s3);
775 static assert(!is(ms1[0])); //narrow strings can't be indexed
776 assert(ms2[0] == '日');
777 assert(ms3[0] == 'H');
778 static assert(!is(ms1[0 .. 1])); //narrow strings can't be sliced
779 assert(equal(ms2[0 .. 2], "日本"w));
780 assert(equal(ms3[0 .. 2], "HE"));
781
782 // Issue 5753
voidFun(int)783 static void voidFun(int) {}
nonvoidFun(int)784 static int nonvoidFun(int) { return 0; }
785 static assert(!__traits(compiles, map!voidFun([1])));
786 static assert(!__traits(compiles, map!(voidFun, voidFun)([1])));
787 static assert(!__traits(compiles, map!(nonvoidFun, voidFun)([1])));
788 static assert(!__traits(compiles, map!(voidFun, nonvoidFun)([1])));
789 static assert(!__traits(compiles, map!(a => voidFun(a))([1])));
790
791 // Phobos issue #15480
792 auto dd = map!(z => z * z, c => c * c * c)([ 1, 2, 3, 4 ]);
793 assert(dd[0] == tuple(1, 1));
794 assert(dd[1] == tuple(4, 8));
795 assert(dd[2] == tuple(9, 27));
796 assert(dd[3] == tuple(16, 64));
797 assert(dd.length == 4);
798 }
799
800 @safe unittest
801 {
802 import std.algorithm.comparison : equal;
803 import std.range;
804 auto LL = iota(1L, 4L);
805 auto m = map!"a*a"(LL);
806 assert(equal(m, [1L, 4L, 9L]));
807 }
808
809 @safe unittest
810 {
811 import std.range : iota;
812
813 // Issue #10130 - map of iota with const step.
814 const step = 2;
815 assert(map!(i => i)(iota(0, 10, step)).walkLength == 5);
816
817 // Need these to all by const to repro the float case, due to the
818 // CommonType template used in the float specialization of iota.
819 const floatBegin = 0.0;
820 const floatEnd = 1.0;
821 const floatStep = 0.02;
822 assert(map!(i => i)(iota(floatBegin, floatEnd, floatStep)).walkLength == 50);
823 }
824
825 @safe unittest
826 {
827 import std.algorithm.comparison : equal;
828 import std.range;
829 //slicing infinites
830 auto rr = iota(0, 5).cycle().map!"a * a"();
831 alias RR = typeof(rr);
832 static assert(hasSlicing!RR);
833 rr = rr[6 .. $]; //Advances 1 cycle and 1 unit
834 assert(equal(rr[0 .. 5], [1, 4, 9, 16, 0]));
835 }
836
837 @safe unittest
838 {
839 import std.range;
840 struct S {int* p;}
841 auto m = immutable(S).init.repeat().map!"a".save;
842 assert(m.front == immutable(S)(null));
843 }
844
845 // each
846 /**
847 Eagerly iterates over $(D r) and calls $(D pred) over _each element.
848
849 If no predicate is specified, $(D each) will default to doing nothing
850 but consuming the entire range. $(D .front) will be evaluated, but this
851 can be avoided by explicitly specifying a predicate lambda with a
852 $(D lazy) parameter.
853
854 $(D each) also supports $(D opApply)-based iterators, so it will work
855 with e.g. $(REF parallel, std,parallelism).
856
857 Params:
858 pred = predicate to apply to each element of the range
859 r = range or iterable over which each iterates
860
861 See_Also: $(REF tee, std,range)
862
863 */
864 template each(alias pred = "a")
865 {
866 import std.meta : AliasSeq;
867 import std.traits : Parameters;
868
869 private:
870 alias BinaryArgs = AliasSeq!(pred, "i", "a");
871
872 enum isRangeUnaryIterable(R) =
873 is(typeof(unaryFun!pred(R.init.front)));
874
875 enum isRangeBinaryIterable(R) =
876 is(typeof(binaryFun!BinaryArgs(0, R.init.front)));
877
878 enum isRangeIterable(R) =
879 isInputRange!R &&
880 (isRangeUnaryIterable!R || isRangeBinaryIterable!R);
881
882 enum isForeachUnaryIterable(R) =
883 is(typeof((R r) {
884 foreach (ref a; r)
885 cast(void) unaryFun!pred(a);
886 }));
887
888 enum isForeachBinaryIterable(R) =
889 is(typeof((R r) {
890 foreach (ref i, ref a; r)
891 cast(void) binaryFun!BinaryArgs(i, a);
892 }));
893
894 enum isForeachIterable(R) =
895 (!isForwardRange!R || isDynamicArray!R) &&
896 (isForeachUnaryIterable!R || isForeachBinaryIterable!R);
897
898 public:
899 void each(Range)(Range r)
900 if (!isForeachIterable!Range && (
901 isRangeIterable!Range ||
902 __traits(compiles, typeof(r.front).length)))
903 {
904 static if (isRangeIterable!Range)
905 {
906 debug(each) pragma(msg, "Using while for ", Range.stringof);
907 static if (isRangeUnaryIterable!Range)
908 {
909 while (!r.empty)
910 {
911 cast(void) unaryFun!pred(r.front);
912 r.popFront();
913 }
914 }
915 else // if (isRangeBinaryIterable!Range)
916 {
917 size_t i = 0;
918 while (!r.empty)
919 {
920 cast(void) binaryFun!BinaryArgs(i, r.front);
921 r.popFront();
922 i++;
923 }
924 }
925 }
926 else
927 {
928 // range interface with >2 parameters.
929 for (auto range = r; !range.empty; range.popFront())
930 pred(range.front.expand);
931 }
932 }
933
934 void each(Iterable)(auto ref Iterable r)
935 if (isForeachIterable!Iterable ||
936 __traits(compiles, Parameters!(Parameters!(r.opApply))))
937 {
938 static if (isForeachIterable!Iterable)
939 {
940 debug(each) pragma(msg, "Using foreach for ", Iterable.stringof);
941 static if (isForeachUnaryIterable!Iterable)
942 {
943 foreach (ref e; r)
944 cast(void) unaryFun!pred(e);
945 }
946 else // if (isForeachBinaryIterable!Iterable)
947 {
948 foreach (ref i, ref e; r)
949 cast(void) binaryFun!BinaryArgs(i, e);
950 }
951 }
952 else
953 {
954 // opApply with >2 parameters. count the delegate args.
955 // only works if it is not templated (otherwise we cannot count the args)
956 auto dg(Parameters!(Parameters!(r.opApply)) params) {
957 pred(params);
958 return 0; // tells opApply to continue iteration
959 }
960 r.opApply(&dg);
961 }
962 }
963 }
964
965 ///
966 @system unittest
967 {
968 import std.range : iota;
969
970 long[] arr;
971 iota(5).each!(n => arr ~= n);
972 assert(arr == [0, 1, 2, 3, 4]);
973
974 // If the range supports it, the value can be mutated in place
975 arr.each!((ref n) => n++);
976 assert(arr == [1, 2, 3, 4, 5]);
977
978 arr.each!"a++";
979 assert(arr == [2, 3, 4, 5, 6]);
980
981 // by-ref lambdas are not allowed for non-ref ranges
982 static assert(!is(typeof(arr.map!(n => n).each!((ref n) => n++))));
983
984 // The default predicate consumes the range
985 auto m = arr.map!(n => n);
986 (&m).each();
987 assert(m.empty);
988
989 // Indexes are also available for in-place mutations
990 arr[] = 0;
991 arr.each!"a=i"();
992 assert(arr == [0, 1, 2, 3, 4]);
993
994 // opApply iterators work as well
995 static class S
996 {
997 int x;
opApply(scope int delegate (ref int _x)dg)998 int opApply(scope int delegate(ref int _x) dg) { return dg(x); }
999 }
1000
1001 auto s = new S;
1002 s.each!"a++";
1003 assert(s.x == 1);
1004 }
1005
1006 // binary foreach with two ref args
1007 @system unittest
1008 {
1009 import std.range : lockstep;
1010
1011 auto a = [ 1, 2, 3 ];
1012 auto b = [ 2, 3, 4 ];
1013
1014 a.lockstep(b).each!((ref x, ref y) { ++x; ++y; });
1015
1016 assert(a == [ 2, 3, 4 ]);
1017 assert(b == [ 3, 4, 5 ]);
1018 }
1019
1020 // #15358: application of `each` with >2 args (opApply)
1021 @system unittest
1022 {
1023 import std.range : lockstep;
1024 auto a = [0,1,2];
1025 auto b = [3,4,5];
1026 auto c = [6,7,8];
1027
1028 lockstep(a, b, c).each!((ref x, ref y, ref z) { ++x; ++y; ++z; });
1029
1030 assert(a == [1,2,3]);
1031 assert(b == [4,5,6]);
1032 assert(c == [7,8,9]);
1033 }
1034
1035 // #15358: application of `each` with >2 args (range interface)
1036 @safe unittest
1037 {
1038 import std.range : zip;
1039 auto a = [0,1,2];
1040 auto b = [3,4,5];
1041 auto c = [6,7,8];
1042
1043 int[] res;
1044
1045 zip(a, b, c).each!((x, y, z) { res ~= x + y + z; });
1046
1047 assert(res == [9, 12, 15]);
1048 }
1049
1050 // #16255: `each` on opApply doesn't support ref
1051 @safe unittest
1052 {
1053 int[] dynamicArray = [1, 2, 3, 4, 5];
1054 int[5] staticArray = [1, 2, 3, 4, 5];
1055
1056 dynamicArray.each!((ref x) => x++);
1057 assert(dynamicArray == [2, 3, 4, 5, 6]);
1058
1059 staticArray.each!((ref x) => x++);
1060 assert(staticArray == [2, 3, 4, 5, 6]);
1061
1062 staticArray[].each!((ref x) => x++);
1063 assert(staticArray == [3, 4, 5, 6, 7]);
1064 }
1065
1066 // #16255: `each` on opApply doesn't support ref
1067 @system unittest
1068 {
1069 struct S
1070 {
1071 int x;
opApplyS1072 int opApply(int delegate(ref int _x) dg) { return dg(x); }
1073 }
1074
1075 S s;
1076 foreach (ref a; s) ++a;
1077 assert(s.x == 1);
1078 s.each!"++a";
1079 assert(s.x == 2);
1080 }
1081
1082 // filter
1083 /**
1084 $(D auto filter(Range)(Range rs) if (isInputRange!(Unqual!Range));)
1085
1086 Implements the higher order _filter function. The predicate is passed to
1087 $(REF unaryFun, std,functional), and can either accept a string, or any callable
1088 that can be executed via $(D pred(element)).
1089
1090 Params:
1091 predicate = Function to apply to each element of range
1092 range = Input range of elements
1093
1094 Returns:
1095 $(D filter!(predicate)(range)) returns a new range containing only elements $(D x) in $(D range) for
1096 which $(D predicate(x)) returns $(D true).
1097
1098 See_Also:
1099 $(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function))
1100 */
1101 template filter(alias predicate)
1102 if (is(typeof(unaryFun!predicate)))
1103 {
1104 auto filter(Range)(Range range) if (isInputRange!(Unqual!Range))
1105 {
1106 return FilterResult!(unaryFun!predicate, Range)(range);
1107 }
1108 }
1109
1110 ///
1111 @safe unittest
1112 {
1113 import std.algorithm.comparison : equal;
1114 import std.math : approxEqual;
1115 import std.range;
1116
1117 int[] arr = [ 1, 2, 3, 4, 5 ];
1118
1119 // Sum all elements
1120 auto small = filter!(a => a < 3)(arr);
1121 assert(equal(small, [ 1, 2 ]));
1122
1123 // Sum again, but with Uniform Function Call Syntax (UFCS)
1124 auto sum = arr.filter!(a => a < 3);
1125 assert(equal(sum, [ 1, 2 ]));
1126
1127 // In combination with chain() to span multiple ranges
1128 int[] a = [ 3, -2, 400 ];
1129 int[] b = [ 100, -101, 102 ];
1130 auto r = chain(a, b).filter!(a => a > 0);
1131 assert(equal(r, [ 3, 400, 100, 102 ]));
1132
1133 // Mixing convertible types is fair game, too
1134 double[] c = [ 2.5, 3.0 ];
1135 auto r1 = chain(c, a, b).filter!(a => cast(int) a != a);
1136 assert(approxEqual(r1, [ 2.5 ]));
1137 }
1138
FilterResult(alias pred,Range)1139 private struct FilterResult(alias pred, Range)
1140 {
1141 alias R = Unqual!Range;
1142 R _input;
1143 private bool _primed;
1144
1145 private void prime()
1146 {
1147 if (_primed) return;
1148 while (!_input.empty && !pred(_input.front))
1149 {
1150 _input.popFront();
1151 }
1152 _primed = true;
1153 }
1154
1155 this(R r)
1156 {
1157 _input = r;
1158 }
1159
1160 private this(R r, bool primed)
1161 {
1162 _input = r;
1163 _primed = primed;
1164 }
1165
1166 auto opSlice() { return this; }
1167
1168 static if (isInfinite!Range)
1169 {
1170 enum bool empty = false;
1171 }
1172 else
1173 {
1174 @property bool empty() { prime; return _input.empty; }
1175 }
1176
1177 void popFront()
1178 {
1179 do
1180 {
1181 _input.popFront();
1182 } while (!_input.empty && !pred(_input.front));
1183 _primed = true;
1184 }
1185
1186 @property auto ref front()
1187 {
1188 prime;
1189 assert(!empty, "Attempting to fetch the front of an empty filter.");
1190 return _input.front;
1191 }
1192
1193 static if (isForwardRange!R)
1194 {
1195 @property auto save()
1196 {
1197 return typeof(this)(_input.save, _primed);
1198 }
1199 }
1200 }
1201
1202 @safe unittest
1203 {
1204 import std.algorithm.comparison : equal;
1205 import std.internal.test.dummyrange;
1206 import std.range;
1207
1208 auto shouldNotLoop4ever = repeat(1).filter!(x => x % 2 == 0);
1209 static assert(isInfinite!(typeof(shouldNotLoop4ever)));
1210 assert(!shouldNotLoop4ever.empty);
1211
1212 int[] a = [ 3, 4, 2 ];
1213 auto r = filter!("a > 3")(a);
1214 static assert(isForwardRange!(typeof(r)));
1215 assert(equal(r, [ 4 ][]));
1216
1217 a = [ 1, 22, 3, 42, 5 ];
1218 auto under10 = filter!("a < 10")(a);
1219 assert(equal(under10, [1, 3, 5][]));
1220 static assert(isForwardRange!(typeof(under10)));
1221 under10.front = 4;
1222 assert(equal(under10, [4, 3, 5][]));
1223 under10.front = 40;
1224 assert(equal(under10, [40, 3, 5][]));
1225 under10.front = 1;
1226
1227 auto infinite = filter!"a > 2"(repeat(3));
1228 static assert(isInfinite!(typeof(infinite)));
1229 static assert(isForwardRange!(typeof(infinite)));
1230 assert(infinite.front == 3);
1231
foreach(DummyType;AllDummyRanges)1232 foreach (DummyType; AllDummyRanges)
1233 {
1234 DummyType d;
1235 auto f = filter!"a & 1"(d);
1236 assert(equal(f, [1,3,5,7,9]));
1237
1238 static if (isForwardRange!DummyType)
1239 {
1240 static assert(isForwardRange!(typeof(f)));
1241 }
1242 }
1243
1244 // With delegates
1245 int x = 10;
overX(int a)1246 int overX(int a) { return a > x; }
getFilter()1247 typeof(filter!overX(a)) getFilter()
1248 {
1249 return filter!overX(a);
1250 }
1251 auto r1 = getFilter();
1252 assert(equal(r1, [22, 42]));
1253
1254 // With chain
1255 auto nums = [0,1,2,3,4];
1256 assert(equal(filter!overX(chain(a, nums)), [22, 42]));
1257
1258 // With copying of inner struct Filter to Map
1259 auto arr = [1,2,3,4,5];
1260 auto m = map!"a + 1"(filter!"a < 4"(arr));
1261 assert(equal(m, [2, 3, 4]));
1262 }
1263
1264 @safe unittest
1265 {
1266 import std.algorithm.comparison : equal;
1267
1268 int[] a = [ 3, 4 ];
1269 const aConst = a;
1270 auto r = filter!("a > 3")(aConst);
1271 assert(equal(r, [ 4 ][]));
1272
1273 a = [ 1, 22, 3, 42, 5 ];
1274 auto under10 = filter!("a < 10")(a);
1275 assert(equal(under10, [1, 3, 5][]));
1276 assert(equal(under10.save, [1, 3, 5][]));
1277 assert(equal(under10.save, under10));
1278 }
1279
1280 @safe unittest
1281 {
1282 import std.algorithm.comparison : equal;
1283 import std.functional : compose, pipe;
1284
1285 assert(equal(compose!(map!"2 * a", filter!"a & 1")([1,2,3,4,5]),
1286 [2,6,10]));
1287 assert(equal(pipe!(filter!"a & 1", map!"2 * a")([1,2,3,4,5]),
1288 [2,6,10]));
1289 }
1290
1291 @safe unittest
1292 {
1293 import std.algorithm.comparison : equal;
1294
1295 int x = 10;
underX(int a)1296 int underX(int a) { return a < x; }
1297 const(int)[] list = [ 1, 2, 10, 11, 3, 4 ];
1298 assert(equal(filter!underX(list), [ 1, 2, 3, 4 ]));
1299 }
1300
1301 /**
1302 * $(D auto filterBidirectional(Range)(Range r) if (isBidirectionalRange!(Unqual!Range));)
1303 *
1304 * Similar to $(D filter), except it defines a
1305 * $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives).
1306 * There is a speed disadvantage - the constructor spends time
1307 * finding the last element in the range that satisfies the filtering
1308 * condition (in addition to finding the first one). The advantage is
1309 * that the filtered range can be spanned from both directions. Also,
1310 * $(REF retro, std,range) can be applied against the filtered range.
1311 *
1312 * The predicate is passed to $(REF unaryFun, std,functional), and can either
1313 * accept a string, or any callable that can be executed via $(D pred(element)).
1314 *
1315 * Params:
1316 * pred = Function to apply to each element of range
1317 * r = Bidirectional range of elements
1318 *
1319 * Returns:
1320 * a new range containing only the elements in r for which pred returns $(D true).
1321 */
filterBidirectional(alias pred)1322 template filterBidirectional(alias pred)
1323 {
1324 auto filterBidirectional(Range)(Range r) if (isBidirectionalRange!(Unqual!Range))
1325 {
1326 return FilterBidiResult!(unaryFun!pred, Range)(r);
1327 }
1328 }
1329
1330 ///
1331 @safe unittest
1332 {
1333 import std.algorithm.comparison : equal;
1334 import std.range;
1335
1336 int[] arr = [ 1, 2, 3, 4, 5 ];
1337 auto small = filterBidirectional!("a < 3")(arr);
1338 static assert(isBidirectionalRange!(typeof(small)));
1339 assert(small.back == 2);
1340 assert(equal(small, [ 1, 2 ]));
1341 assert(equal(retro(small), [ 2, 1 ]));
1342 // In combination with chain() to span multiple ranges
1343 int[] a = [ 3, -2, 400 ];
1344 int[] b = [ 100, -101, 102 ];
1345 auto r = filterBidirectional!("a > 0")(chain(a, b));
1346 assert(r.back == 102);
1347 }
1348
FilterBidiResult(alias pred,Range)1349 private struct FilterBidiResult(alias pred, Range)
1350 {
1351 alias R = Unqual!Range;
1352 R _input;
1353
1354 this(R r)
1355 {
1356 _input = r;
1357 while (!_input.empty && !pred(_input.front)) _input.popFront();
1358 while (!_input.empty && !pred(_input.back)) _input.popBack();
1359 }
1360
1361 @property bool empty() { return _input.empty; }
1362
1363 void popFront()
1364 {
1365 do
1366 {
1367 _input.popFront();
1368 } while (!_input.empty && !pred(_input.front));
1369 }
1370
1371 @property auto ref front()
1372 {
1373 assert(!empty, "Attempting to fetch the front of an empty filterBidirectional.");
1374 return _input.front;
1375 }
1376
1377 void popBack()
1378 {
1379 do
1380 {
1381 _input.popBack();
1382 } while (!_input.empty && !pred(_input.back));
1383 }
1384
1385 @property auto ref back()
1386 {
1387 assert(!empty, "Attempting to fetch the back of an empty filterBidirectional.");
1388 return _input.back;
1389 }
1390
1391 @property auto save()
1392 {
1393 return typeof(this)(_input.save);
1394 }
1395 }
1396
1397 /**
1398 Groups consecutively equivalent elements into a single tuple of the element and
1399 the number of its repetitions.
1400
1401 Similarly to $(D uniq), $(D group) produces a range that iterates over unique
1402 consecutive elements of the given range. Each element of this range is a tuple
1403 of the element and the number of times it is repeated in the original range.
1404 Equivalence of elements is assessed by using the predicate $(D pred), which
1405 defaults to $(D "a == b"). The predicate is passed to $(REF binaryFun, std,functional),
1406 and can either accept a string, or any callable that can be executed via
1407 $(D pred(element, element)).
1408
1409 Params:
1410 pred = Binary predicate for determining equivalence of two elements.
1411 r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to
1412 iterate over.
1413
1414 Returns: A range of elements of type $(D Tuple!(ElementType!R, uint)),
1415 representing each consecutively unique element and its respective number of
1416 occurrences in that run. This will be an input range if $(D R) is an input
1417 range, and a forward range in all other cases.
1418
1419 See_Also: $(LREF chunkBy), which chunks an input range into subranges
1420 of equivalent adjacent elements.
1421 */
1422 Group!(pred, Range) group(alias pred = "a == b", Range)(Range r)
1423 {
1424 return typeof(return)(r);
1425 }
1426
1427 /// ditto
1428 struct Group(alias pred, R)
1429 if (isInputRange!R)
1430 {
1431 import std.typecons : Rebindable, tuple, Tuple;
1432
1433 private alias comp = binaryFun!pred;
1434
1435 private alias E = ElementType!R;
1436 static if ((is(E == class) || is(E == interface)) &&
1437 (is(E == const) || is(E == immutable)))
1438 {
1439 private alias MutableE = Rebindable!E;
1440 }
1441 else static if (is(E : Unqual!E))
1442 {
1443 private alias MutableE = Unqual!E;
1444 }
1445 else
1446 {
1447 private alias MutableE = E;
1448 }
1449
1450 private R _input;
1451 private Tuple!(MutableE, uint) _current;
1452
1453 ///
this(R input)1454 this(R input)
1455 {
1456 _input = input;
1457 if (!_input.empty) popFront();
1458 }
1459
1460 ///
popFront()1461 void popFront()
1462 {
1463 if (_input.empty)
1464 {
1465 _current[1] = 0;
1466 }
1467 else
1468 {
1469 _current = tuple(_input.front, 1u);
1470 _input.popFront();
1471 while (!_input.empty && comp(_current[0], _input.front))
1472 {
1473 ++_current[1];
1474 _input.popFront();
1475 }
1476 }
1477 }
1478
1479 static if (isInfinite!R)
1480 {
1481 ///
1482 enum bool empty = false; // Propagate infiniteness.
1483 }
1484 else
1485 {
1486 ///
empty()1487 @property bool empty()
1488 {
1489 return _current[1] == 0;
1490 }
1491 }
1492
1493 ///
front()1494 @property auto ref front()
1495 {
1496 assert(!empty, "Attempting to fetch the front of an empty Group.");
1497 return _current;
1498 }
1499
1500 static if (isForwardRange!R)
1501 {
1502 ///
typeof(this)1503 @property typeof(this) save() {
1504 typeof(this) ret = this;
1505 ret._input = this._input.save;
1506 ret._current = this._current;
1507 return ret;
1508 }
1509 }
1510 }
1511
1512 ///
1513 @safe unittest
1514 {
1515 import std.algorithm.comparison : equal;
1516 import std.typecons : tuple, Tuple;
1517
1518 int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
1519 assert(equal(group(arr), [ tuple(1, 1u), tuple(2, 4u), tuple(3, 1u),
1520 tuple(4, 3u), tuple(5, 1u) ][]));
1521 }
1522
1523 /**
1524 * Using group, an associative array can be easily generated with the count of each
1525 * unique element in the range.
1526 */
1527 @safe unittest
1528 {
1529 import std.algorithm.sorting : sort;
1530 import std.array : assocArray;
1531
1532 uint[string] result;
1533 auto range = ["a", "b", "a", "c", "b", "c", "c", "d", "e"];
1534 result = range.sort!((a, b) => a < b)
1535 .group
1536 .assocArray;
1537
1538 assert(result == ["a": 2U, "b": 2U, "c": 3U, "d": 1U, "e": 1U]);
1539 }
1540
1541 @safe unittest
1542 {
1543 import std.algorithm.comparison : equal;
1544 import std.internal.test.dummyrange;
1545 import std.typecons : tuple, Tuple;
1546
1547 int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
1548 assert(equal(group(arr), [ tuple(1, 1u), tuple(2, 4u), tuple(3, 1u),
1549 tuple(4, 3u), tuple(5, 1u) ][]));
1550 static assert(isForwardRange!(typeof(group(arr))));
1551
foreach(DummyType;AllDummyRanges)1552 foreach (DummyType; AllDummyRanges)
1553 {
1554 DummyType d;
1555 auto g = group(d);
1556
1557 static assert(d.rt == RangeType.Input || isForwardRange!(typeof(g)));
1558
1559 assert(equal(g, [tuple(1, 1u), tuple(2, 1u), tuple(3, 1u), tuple(4, 1u),
1560 tuple(5, 1u), tuple(6, 1u), tuple(7, 1u), tuple(8, 1u),
1561 tuple(9, 1u), tuple(10, 1u)]));
1562 }
1563 }
1564
1565 @safe unittest
1566 {
1567 import std.algorithm.comparison : equal;
1568 import std.typecons : tuple;
1569
1570 // Issue 13857
1571 immutable(int)[] a1 = [1,1,2,2,2,3,4,4,5,6,6,7,8,9,9,9];
1572 auto g1 = group(a1);
1573 assert(equal(g1, [ tuple(1, 2u), tuple(2, 3u), tuple(3, 1u),
1574 tuple(4, 2u), tuple(5, 1u), tuple(6, 2u),
1575 tuple(7, 1u), tuple(8, 1u), tuple(9, 3u)
1576 ]));
1577
1578 // Issue 13162
1579 immutable(ubyte)[] a2 = [1, 1, 1, 0, 0, 0];
1580 auto g2 = a2.group;
1581 assert(equal(g2, [ tuple(1, 3u), tuple(0, 3u) ]));
1582
1583 // Issue 10104
1584 const a3 = [1, 1, 2, 2];
1585 auto g3 = a3.group;
1586 assert(equal(g3, [ tuple(1, 2u), tuple(2, 2u) ]));
1587
1588 interface I {}
1589 class C : I {}
1590 const C[] a4 = [new const C()];
1591 auto g4 = a4.group!"a is b";
1592 assert(g4.front[1] == 1);
1593
1594 immutable I[] a5 = [new immutable C()];
1595 auto g5 = a5.group!"a is b";
1596 assert(g5.front[1] == 1);
1597
1598 const(int[][]) a6 = [[1], [1]];
1599 auto g6 = a6.group;
1600 assert(equal(g6.front[0], [1]));
1601 }
1602
1603 // Used by implementation of chunkBy for non-forward input ranges.
1604 private struct ChunkByChunkImpl(alias pred, Range)
1605 if (isInputRange!Range && !isForwardRange!Range)
1606 {
1607 alias fun = binaryFun!pred;
1608
1609 private Range r;
1610 private ElementType!Range prev;
1611
1612 this(Range range, ElementType!Range _prev)
1613 {
1614 r = range;
1615 prev = _prev;
1616 }
1617
empty()1618 @property bool empty()
1619 {
1620 return r.empty || !fun(prev, r.front);
1621 }
1622
1623 @property ElementType!Range front() { return r.front; }
popFront()1624 void popFront() { r.popFront(); }
1625 }
1626
ChunkByImplIsUnary(alias pred,Range)1627 private template ChunkByImplIsUnary(alias pred, Range)
1628 {
1629 static if (is(typeof(binaryFun!pred(ElementType!Range.init,
1630 ElementType!Range.init)) : bool))
1631 enum ChunkByImplIsUnary = false;
1632 else static if (is(typeof(
1633 unaryFun!pred(ElementType!Range.init) ==
1634 unaryFun!pred(ElementType!Range.init))))
1635 enum ChunkByImplIsUnary = true;
1636 else
1637 static assert(0, "chunkBy expects either a binary predicate or "~
1638 "a unary predicate on range elements of type: "~
1639 ElementType!Range.stringof);
1640 }
1641
1642 // Implementation of chunkBy for non-forward input ranges.
1643 private struct ChunkByImpl(alias pred, Range)
1644 if (isInputRange!Range && !isForwardRange!Range)
1645 {
1646 enum bool isUnary = ChunkByImplIsUnary!(pred, Range);
1647
1648 static if (isUnary)
1649 alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b));
1650 else
1651 alias eq = binaryFun!pred;
1652
1653 private Range r;
1654 private ElementType!Range _prev;
1655
this(Range _r)1656 this(Range _r)
1657 {
1658 r = _r;
1659 if (!empty)
1660 {
1661 // Check reflexivity if predicate is claimed to be an equivalence
1662 // relation.
1663 assert(eq(r.front, r.front),
1664 "predicate is not reflexive");
1665
1666 // _prev's type may be a nested struct, so must be initialized
1667 // directly in the constructor (cannot call savePred()).
1668 _prev = r.front;
1669 }
1670 else
1671 {
1672 // We won't use _prev, but must be initialized.
1673 _prev = typeof(_prev).init;
1674 }
1675 }
empty()1676 @property bool empty() { return r.empty; }
1677
front()1678 @property auto front()
1679 {
1680 static if (isUnary)
1681 {
1682 import std.typecons : tuple;
1683 return tuple(unaryFun!pred(_prev),
1684 ChunkByChunkImpl!(eq, Range)(r, _prev));
1685 }
1686 else
1687 {
1688 return ChunkByChunkImpl!(eq, Range)(r, _prev);
1689 }
1690 }
1691
popFront()1692 void popFront()
1693 {
1694 while (!r.empty)
1695 {
1696 if (!eq(_prev, r.front))
1697 {
1698 _prev = r.front;
1699 break;
1700 }
1701 r.popFront();
1702 }
1703 }
1704 }
1705
1706 // Single-pass implementation of chunkBy for forward ranges.
1707 private struct ChunkByImpl(alias pred, Range)
1708 if (isForwardRange!Range)
1709 {
1710 import std.typecons : RefCounted;
1711
1712 enum bool isUnary = ChunkByImplIsUnary!(pred, Range);
1713
1714 static if (isUnary)
1715 alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b));
1716 else
1717 alias eq = binaryFun!pred;
1718
1719 // Outer range
1720 static struct Impl
1721 {
1722 size_t groupNum;
1723 Range current;
1724 Range next;
1725 }
1726
1727 // Inner range
1728 static struct Group
1729 {
1730 private size_t groupNum;
1731 private Range start;
1732 private Range current;
1733
1734 private RefCounted!Impl mothership;
1735
1736 this(RefCounted!Impl origin)
1737 {
1738 groupNum = origin.groupNum;
1739
1740 start = origin.current.save;
1741 current = origin.current.save;
1742 assert(!start.empty);
1743
1744 mothership = origin;
1745
1746 // Note: this requires reflexivity.
1747 assert(eq(start.front, current.front),
1748 "predicate is not reflexive");
1749 }
1750
empty()1751 @property bool empty() { return groupNum == size_t.max; }
front()1752 @property auto ref front() { return current.front; }
1753
popFront()1754 void popFront()
1755 {
1756 current.popFront();
1757
1758 // Note: this requires transitivity.
1759 if (current.empty || !eq(start.front, current.front))
1760 {
1761 if (groupNum == mothership.groupNum)
1762 {
1763 // If parent range hasn't moved on yet, help it along by
1764 // saving location of start of next Group.
1765 mothership.next = current.save;
1766 }
1767
1768 groupNum = size_t.max;
1769 }
1770 }
1771
save()1772 @property auto save()
1773 {
1774 auto copy = this;
1775 copy.current = current.save;
1776 return copy;
1777 }
1778 }
1779 static assert(isForwardRange!Group);
1780
1781 private RefCounted!Impl impl;
1782
this(Range r)1783 this(Range r)
1784 {
1785 impl = RefCounted!Impl(0, r, r.save);
1786 }
1787
empty()1788 @property bool empty() { return impl.current.empty; }
1789
front()1790 @property auto front()
1791 {
1792 static if (isUnary)
1793 {
1794 import std.typecons : tuple;
1795 return tuple(unaryFun!pred(impl.current.front), Group(impl));
1796 }
1797 else
1798 {
1799 return Group(impl);
1800 }
1801 }
1802
popFront()1803 void popFront()
1804 {
1805 // Scan for next group. If we're lucky, one of our Groups would have
1806 // already set .next to the start of the next group, in which case the
1807 // loop is skipped.
1808 while (!impl.next.empty && eq(impl.current.front, impl.next.front))
1809 {
1810 impl.next.popFront();
1811 }
1812
1813 impl.current = impl.next.save;
1814
1815 // Indicate to any remaining Groups that we have moved on.
1816 impl.groupNum++;
1817 }
1818
save()1819 @property auto save()
1820 {
1821 // Note: the new copy of the range will be detached from any existing
1822 // satellite Groups, and will not benefit from the .next acceleration.
1823 return typeof(this)(impl.current.save);
1824 }
1825
1826 static assert(isForwardRange!(typeof(this)));
1827 }
1828
1829 @system unittest
1830 {
1831 import std.algorithm.comparison : equal;
1832
1833 size_t popCount = 0;
1834 class RefFwdRange
1835 {
1836 int[] impl;
1837
1838 @safe nothrow:
1839
this(int[]data)1840 this(int[] data) { impl = data; }
empty()1841 @property bool empty() { return impl.empty; }
front()1842 @property auto ref front() { return impl.front; }
popFront()1843 void popFront()
1844 {
1845 impl.popFront();
1846 popCount++;
1847 }
save()1848 @property auto save() { return new RefFwdRange(impl); }
1849 }
1850 static assert(isForwardRange!RefFwdRange);
1851
1852 auto testdata = new RefFwdRange([1, 3, 5, 2, 4, 7, 6, 8, 9]);
1853 auto groups = testdata.chunkBy!((a,b) => (a % 2) == (b % 2));
1854 auto outerSave1 = groups.save;
1855
1856 // Sanity test
1857 assert(groups.equal!equal([[1, 3, 5], [2, 4], [7], [6, 8], [9]]));
1858 assert(groups.empty);
1859
1860 // Performance test for single-traversal use case: popFront should not have
1861 // been called more times than there are elements if we traversed the
1862 // segmented range exactly once.
1863 assert(popCount == 9);
1864
1865 // Outer range .save test
1866 groups = outerSave1.save;
1867 assert(!groups.empty);
1868
1869 // Inner range .save test
1870 auto grp1 = groups.front.save;
1871 auto grp1b = grp1.save;
1872 assert(grp1b.equal([1, 3, 5]));
1873 assert(grp1.save.equal([1, 3, 5]));
1874
1875 // Inner range should remain consistent after outer range has moved on.
1876 groups.popFront();
1877 assert(grp1.save.equal([1, 3, 5]));
1878
1879 // Inner range should not be affected by subsequent inner ranges.
1880 assert(groups.front.equal([2, 4]));
1881 assert(grp1.save.equal([1, 3, 5]));
1882 }
1883
1884 /**
1885 * Chunks an input range into subranges of equivalent adjacent elements.
1886 * In other languages this is often called `partitionBy`, `groupBy`
1887 * or `sliceWhen`.
1888 *
1889 * Equivalence is defined by the predicate $(D pred), which can be either
1890 * binary, which is passed to $(REF binaryFun, std,functional), or unary, which is
1891 * passed to $(REF unaryFun, std,functional). In the binary form, two _range elements
1892 * $(D a) and $(D b) are considered equivalent if $(D pred(a,b)) is true. In
1893 * unary form, two elements are considered equivalent if $(D pred(a) == pred(b))
1894 * is true.
1895 *
1896 * This predicate must be an equivalence relation, that is, it must be
1897 * reflexive ($(D pred(x,x)) is always true), symmetric
1898 * ($(D pred(x,y) == pred(y,x))), and transitive ($(D pred(x,y) && pred(y,z))
1899 * implies $(D pred(x,z))). If this is not the case, the range returned by
1900 * chunkBy may assert at runtime or behave erratically.
1901 *
1902 * Params:
1903 * pred = Predicate for determining equivalence.
1904 * r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be chunked.
1905 *
1906 * Returns: With a binary predicate, a range of ranges is returned in which
1907 * all elements in a given subrange are equivalent under the given predicate.
1908 * With a unary predicate, a range of tuples is returned, with the tuple
1909 * consisting of the result of the unary predicate for each subrange, and the
1910 * subrange itself.
1911 *
1912 * Notes:
1913 *
1914 * Equivalent elements separated by an intervening non-equivalent element will
1915 * appear in separate subranges; this function only considers adjacent
1916 * equivalence. Elements in the subranges will always appear in the same order
1917 * they appear in the original range.
1918 *
1919 * See_also:
1920 * $(LREF group), which collapses adjacent equivalent elements into a single
1921 * element.
1922 */
1923 auto chunkBy(alias pred, Range)(Range r)
1924 if (isInputRange!Range)
1925 {
1926 return ChunkByImpl!(pred, Range)(r);
1927 }
1928
1929 /// Showing usage with binary predicate:
1930 /*FIXME: @safe*/ @system unittest
1931 {
1932 import std.algorithm.comparison : equal;
1933
1934 // Grouping by particular attribute of each element:
1935 auto data = [
1936 [1, 1],
1937 [1, 2],
1938 [2, 2],
1939 [2, 3]
1940 ];
1941
1942 auto r1 = data.chunkBy!((a,b) => a[0] == b[0]);
1943 assert(r1.equal!equal([
1944 [[1, 1], [1, 2]],
1945 [[2, 2], [2, 3]]
1946 ]));
1947
1948 auto r2 = data.chunkBy!((a,b) => a[1] == b[1]);
1949 assert(r2.equal!equal([
1950 [[1, 1]],
1951 [[1, 2], [2, 2]],
1952 [[2, 3]]
1953 ]));
1954 }
1955
version(none)1956 version (none) // this example requires support for non-equivalence relations
1957 @safe unittest
1958 {
1959 // Grouping by maximum adjacent difference:
1960 import std.math : abs;
1961 auto r3 = [1, 3, 2, 5, 4, 9, 10].chunkBy!((a, b) => abs(a-b) < 3);
1962 assert(r3.equal!equal([
1963 [1, 3, 2],
1964 [5, 4],
1965 [9, 10]
1966 ]));
1967
1968 }
1969
1970 /// Showing usage with unary predicate:
1971 /* FIXME: pure @safe nothrow*/ @system unittest
1972 {
1973 import std.algorithm.comparison : equal;
1974 import std.range.primitives;
1975 import std.typecons : tuple;
1976
1977 // Grouping by particular attribute of each element:
1978 auto range =
1979 [
1980 [1, 1],
1981 [1, 1],
1982 [1, 2],
1983 [2, 2],
1984 [2, 3],
1985 [2, 3],
1986 [3, 3]
1987 ];
1988
1989 auto byX = chunkBy!(a => a[0])(range);
1990 auto expected1 =
1991 [
1992 tuple(1, [[1, 1], [1, 1], [1, 2]]),
1993 tuple(2, [[2, 2], [2, 3], [2, 3]]),
1994 tuple(3, [[3, 3]])
1995 ];
foreach(e;byX)1996 foreach (e; byX)
1997 {
1998 assert(!expected1.empty);
1999 assert(e[0] == expected1.front[0]);
2000 assert(e[1].equal(expected1.front[1]));
2001 expected1.popFront();
2002 }
2003
2004 auto byY = chunkBy!(a => a[1])(range);
2005 auto expected2 =
2006 [
2007 tuple(1, [[1, 1], [1, 1]]),
2008 tuple(2, [[1, 2], [2, 2]]),
2009 tuple(3, [[2, 3], [2, 3], [3, 3]])
2010 ];
foreach(e;byY)2011 foreach (e; byY)
2012 {
2013 assert(!expected2.empty);
2014 assert(e[0] == expected2.front[0]);
2015 assert(e[1].equal(expected2.front[1]));
2016 expected2.popFront();
2017 }
2018 }
2019
2020 /*FIXME: pure @safe nothrow*/ @system unittest
2021 {
2022 import std.algorithm.comparison : equal;
2023 import std.typecons : tuple;
2024
2025 struct Item { int x, y; }
2026
2027 // Force R to have only an input range API with reference semantics, so
2028 // that we're not unknowingly making use of array semantics outside of the
2029 // range API.
RefInputRange(R)2030 class RefInputRange(R)
2031 {
2032 R data;
2033 this(R _data) pure @safe nothrow { data = _data; }
2034 @property bool empty() pure @safe nothrow { return data.empty; }
2035 @property auto front() pure @safe nothrow { return data.front; }
2036 void popFront() pure @safe nothrow { data.popFront(); }
2037 }
refInputRange(R)2038 auto refInputRange(R)(R range) { return new RefInputRange!R(range); }
2039
2040 {
2041 auto arr = [ Item(1,2), Item(1,3), Item(2,3) ];
2042 static assert(isForwardRange!(typeof(arr)));
2043
2044 auto byX = chunkBy!(a => a.x)(arr);
2045 static assert(isForwardRange!(typeof(byX)));
2046
2047 auto byX_subrange1 = byX.front[1].save;
2048 auto byX_subrange2 = byX.front[1].save;
2049 static assert(isForwardRange!(typeof(byX_subrange1)));
2050 static assert(isForwardRange!(typeof(byX_subrange2)));
2051
2052 byX.popFront();
2053 assert(byX_subrange1.equal([ Item(1,2), Item(1,3) ]));
2054 byX_subrange1.popFront();
2055 assert(byX_subrange1.equal([ Item(1,3) ]));
2056 assert(byX_subrange2.equal([ Item(1,2), Item(1,3) ]));
2057
2058 auto byY = chunkBy!(a => a.y)(arr);
2059 static assert(isForwardRange!(typeof(byY)));
2060
2061 auto byY2 = byY.save;
2062 static assert(is(typeof(byY) == typeof(byY2)));
2063 byY.popFront();
2064 assert(byY.front[0] == 3);
2065 assert(byY.front[1].equal([ Item(1,3), Item(2,3) ]));
2066 assert(byY2.front[0] == 2);
2067 assert(byY2.front[1].equal([ Item(1,2) ]));
2068 }
2069
2070 // Test non-forward input ranges.
2071 {
2072 auto range = refInputRange([ Item(1,1), Item(1,2), Item(2,2) ]);
2073 auto byX = chunkBy!(a => a.x)(range);
2074 assert(byX.front[0] == 1);
2075 assert(byX.front[1].equal([ Item(1,1), Item(1,2) ]));
2076 byX.popFront();
2077 assert(byX.front[0] == 2);
2078 assert(byX.front[1].equal([ Item(2,2) ]));
2079 byX.popFront();
2080 assert(byX.empty);
2081 assert(range.empty);
2082
2083 range = refInputRange([ Item(1,1), Item(1,2), Item(2,2) ]);
2084 auto byY = chunkBy!(a => a.y)(range);
2085 assert(byY.front[0] == 1);
2086 assert(byY.front[1].equal([ Item(1,1) ]));
2087 byY.popFront();
2088 assert(byY.front[0] == 2);
2089 assert(byY.front[1].equal([ Item(1,2), Item(2,2) ]));
2090 byY.popFront();
2091 assert(byY.empty);
2092 assert(range.empty);
2093 }
2094 }
2095
2096 // Issue 13595
version(none)2097 version (none) // This requires support for non-equivalence relations
2098 @system unittest
2099 {
2100 import std.algorithm.comparison : equal;
2101 auto r = [1, 2, 3, 4, 5, 6, 7, 8, 9].chunkBy!((x, y) => ((x*y) % 3) == 0);
2102 assert(r.equal!equal([
2103 [1],
2104 [2, 3, 4],
2105 [5, 6, 7],
2106 [8, 9]
2107 ]));
2108 }
2109
2110 // Issue 13805
2111 @system unittest
2112 {
2113 [""].map!((s) => s).chunkBy!((x, y) => true);
2114 }
2115
2116 // joiner
2117 /**
2118 Lazily joins a range of ranges with a separator. The separator itself
2119 is a range. If a separator is not provided, then the ranges are
2120 joined directly without anything in between them (often called `flatten`
2121 in other languages).
2122
2123 Params:
2124 r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of input
2125 ranges to be joined.
2126 sep = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) of
2127 element(s) to serve as separators in the joined range.
2128
2129 Returns:
2130 A range of elements in the joined range. This will be a forward range if
2131 both outer and inner ranges of $(D RoR) are forward ranges; otherwise it will
2132 be only an input range.
2133
2134 See_also:
2135 $(REF chain, std,range), which chains a sequence of ranges with compatible elements
2136 into a single range.
2137 */
2138 auto joiner(RoR, Separator)(RoR r, Separator sep)
2139 if (isInputRange!RoR && isInputRange!(ElementType!RoR)
2140 && isForwardRange!Separator
2141 && is(ElementType!Separator : ElementType!(ElementType!RoR)))
2142 {
2143 static struct Result
2144 {
2145 private RoR _items;
2146 private ElementType!RoR _current;
2147 private Separator _sep, _currentSep;
2148
2149 // This is a mixin instead of a function for the following reason (as
2150 // explained by Kenji Hara): "This is necessary from 2.061. If a
2151 // struct has a nested struct member, it must be directly initialized
2152 // in its constructor to avoid leaving undefined state. If you change
2153 // setItem to a function, the initialization of _current field is
2154 // wrapped into private member function, then compiler could not detect
2155 // that is correctly initialized while constructing. To avoid the
2156 // compiler error check, string mixin is used."
2157 private enum setItem =
2158 q{
2159 if (!_items.empty)
2160 {
2161 // If we're exporting .save, we must not consume any of the
2162 // subranges, since RoR.save does not guarantee that the states
2163 // of the subranges are also saved.
2164 static if (isForwardRange!RoR &&
2165 isForwardRange!(ElementType!RoR))
2166 _current = _items.front.save;
2167 else
2168 _current = _items.front;
2169 }
2170 };
2171
useSeparatorResult2172 private void useSeparator()
2173 {
2174 // Separator must always come after an item.
2175 assert(_currentSep.empty && !_items.empty,
2176 "joiner: internal error");
2177 _items.popFront();
2178
2179 // If there are no more items, we're done, since separators are not
2180 // terminators.
2181 if (_items.empty) return;
2182
2183 if (_sep.empty)
2184 {
2185 // Advance to the next range in the
2186 // input
2187 while (_items.front.empty)
2188 {
2189 _items.popFront();
2190 if (_items.empty) return;
2191 }
2192 mixin(setItem);
2193 }
2194 else
2195 {
2196 _currentSep = _sep.save;
2197 assert(!_currentSep.empty);
2198 }
2199 }
2200
2201 private enum useItem =
2202 q{
2203 // FIXME: this will crash if either _currentSep or _current are
2204 // class objects, because .init is null when the ctor invokes this
2205 // mixin.
2206 //assert(_currentSep.empty && _current.empty,
2207 // "joiner: internal error");
2208
2209 // Use the input
2210 if (_items.empty) return;
2211 mixin(setItem);
2212 if (_current.empty)
2213 {
2214 // No data in the current item - toggle to use the separator
2215 useSeparator();
2216 }
2217 };
2218
thisResult2219 this(RoR items, Separator sep)
2220 {
2221 _items = items;
2222 _sep = sep;
2223
2224 //mixin(useItem); // _current should be initialized in place
2225 if (_items.empty)
2226 _current = _current.init; // set invalid state
2227 else
2228 {
2229 // If we're exporting .save, we must not consume any of the
2230 // subranges, since RoR.save does not guarantee that the states
2231 // of the subranges are also saved.
2232 static if (isForwardRange!RoR &&
2233 isForwardRange!(ElementType!RoR))
2234 _current = _items.front.save;
2235 else
2236 _current = _items.front;
2237
2238 if (_current.empty)
2239 {
2240 // No data in the current item - toggle to use the separator
2241 useSeparator();
2242 }
2243 }
2244 }
2245
emptyResult2246 @property auto empty()
2247 {
2248 return _items.empty;
2249 }
2250
2251 @property ElementType!(ElementType!RoR) front()
2252 {
2253 if (!_currentSep.empty) return _currentSep.front;
2254 assert(!_current.empty, "Attempting to fetch the front of an empty joiner.");
2255 return _current.front;
2256 }
2257
popFrontResult2258 void popFront()
2259 {
2260 assert(!_items.empty, "Attempting to popFront an empty joiner.");
2261 // Using separator?
2262 if (!_currentSep.empty)
2263 {
2264 _currentSep.popFront();
2265 if (!_currentSep.empty) return;
2266 mixin(useItem);
2267 }
2268 else
2269 {
2270 // we're using the range
2271 _current.popFront();
2272 if (!_current.empty) return;
2273 useSeparator();
2274 }
2275 }
2276
2277 static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
2278 {
saveResult2279 @property auto save()
2280 {
2281 Result copy = this;
2282 copy._items = _items.save;
2283 copy._current = _current.save;
2284 copy._sep = _sep.save;
2285 copy._currentSep = _currentSep.save;
2286 return copy;
2287 }
2288 }
2289 }
2290 return Result(r, sep);
2291 }
2292
2293 ///
2294 @safe unittest
2295 {
2296 import std.algorithm.comparison : equal;
2297 import std.conv : text;
2298
2299 assert(["abc", "def"].joiner.equal("abcdef"));
2300 assert(["Mary", "has", "a", "little", "lamb"]
2301 .joiner("...")
2302 .equal("Mary...has...a...little...lamb"));
2303 assert(["", "abc"].joiner("xyz").equal("xyzabc"));
2304 assert([""].joiner("xyz").equal(""));
2305 assert(["", ""].joiner("xyz").equal("xyz"));
2306 }
2307
2308 @system unittest
2309 {
2310 import std.algorithm.comparison : equal;
2311 import std.range.interfaces;
2312 import std.range.primitives;
2313 // joiner() should work for non-forward ranges too.
2314 auto r = inputRangeObject(["abc", "def"]);
2315 assert(equal(joiner(r, "xyz"), "abcxyzdef"));
2316 }
2317
2318 @system unittest
2319 {
2320 import std.algorithm.comparison : equal;
2321 import std.range;
2322
2323 // Related to issue 8061
2324 auto r = joiner([
2325 inputRangeObject("abc"),
2326 inputRangeObject("def"),
2327 ], "-*-");
2328
2329 assert(equal(r, "abc-*-def"));
2330
2331 // Test case where separator is specified but is empty.
2332 auto s = joiner([
2333 inputRangeObject("abc"),
2334 inputRangeObject("def"),
2335 ], "");
2336
2337 assert(equal(s, "abcdef"));
2338
2339 // Test empty separator with some empty elements
2340 auto t = joiner([
2341 inputRangeObject("abc"),
2342 inputRangeObject(""),
2343 inputRangeObject("def"),
2344 inputRangeObject(""),
2345 ], "");
2346
2347 assert(equal(t, "abcdef"));
2348
2349 // Test empty elements with non-empty separator
2350 auto u = joiner([
2351 inputRangeObject(""),
2352 inputRangeObject("abc"),
2353 inputRangeObject(""),
2354 inputRangeObject("def"),
2355 inputRangeObject(""),
2356 ], "+-");
2357
2358 assert(equal(u, "+-abc+-+-def+-"));
2359
2360 // Issue 13441: only(x) as separator
2361 string[][] lines = [null];
2362 lines
2363 .joiner(only("b"))
2364 .array();
2365 }
2366
2367 @safe unittest
2368 {
2369 import std.algorithm.comparison : equal;
2370
2371 // Transience correctness test
2372 struct TransientRange
2373 {
2374 @safe:
2375 int[][] src;
2376 int[] buf;
2377
thisTransientRange2378 this(int[][] _src)
2379 {
2380 src = _src;
2381 buf.length = 100;
2382 }
emptyTransientRange2383 @property bool empty() { return src.empty; }
frontTransientRange2384 @property int[] front()
2385 {
2386 assert(src.front.length <= buf.length);
2387 buf[0 .. src.front.length] = src.front[0..$];
2388 return buf[0 .. src.front.length];
2389 }
popFrontTransientRange2390 void popFront() { src.popFront(); }
2391 }
2392
2393 // Test embedded empty elements
2394 auto tr1 = TransientRange([[], [1,2,3], [], [4]]);
2395 assert(equal(joiner(tr1, [0]), [0,1,2,3,0,0,4]));
2396
2397 // Test trailing empty elements
2398 auto tr2 = TransientRange([[], [1,2,3], []]);
2399 assert(equal(joiner(tr2, [0]), [0,1,2,3,0]));
2400
2401 // Test no empty elements
2402 auto tr3 = TransientRange([[1,2], [3,4]]);
2403 assert(equal(joiner(tr3, [0,1]), [1,2,0,1,3,4]));
2404
2405 // Test consecutive empty elements
2406 auto tr4 = TransientRange([[1,2], [], [], [], [3,4]]);
2407 assert(equal(joiner(tr4, [0,1]), [1,2,0,1,0,1,0,1,0,1,3,4]));
2408
2409 // Test consecutive trailing empty elements
2410 auto tr5 = TransientRange([[1,2], [3,4], [], []]);
2411 assert(equal(joiner(tr5, [0,1]), [1,2,0,1,3,4,0,1,0,1]));
2412 }
2413
2414 @safe unittest
2415 {
2416 static assert(isInputRange!(typeof(joiner([""], ""))));
2417 static assert(isForwardRange!(typeof(joiner([""], ""))));
2418 }
2419
2420 /// Ditto
2421 auto joiner(RoR)(RoR r)
2422 if (isInputRange!RoR && isInputRange!(ElementType!RoR))
2423 {
2424 static struct Result
2425 {
2426 private:
2427 RoR _items;
2428 ElementType!RoR _current;
2429 enum prepare =
2430 q{
2431 // Skip over empty subranges.
2432 if (_items.empty) return;
2433 while (_items.front.empty)
2434 {
2435 _items.popFront();
2436 if (_items.empty) return;
2437 }
2438 // We cannot export .save method unless we ensure subranges are not
2439 // consumed when a .save'd copy of ourselves is iterated over. So
2440 // we need to .save each subrange we traverse.
2441 static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
2442 _current = _items.front.save;
2443 else
2444 _current = _items.front;
2445 };
2446 this(RoR items, ElementType!RoR current)
2447 {
2448 _items = items;
2449 _current = current;
2450 }
2451 public:
thisResult2452 this(RoR r)
2453 {
2454 _items = r;
2455 //mixin(prepare); // _current should be initialized in place
2456
2457 // Skip over empty subranges.
2458 while (!_items.empty && _items.front.empty)
2459 _items.popFront();
2460
2461 if (_items.empty)
2462 _current = _current.init; // set invalid state
2463 else
2464 {
2465 // We cannot export .save method unless we ensure subranges are not
2466 // consumed when a .save'd copy of ourselves is iterated over. So
2467 // we need to .save each subrange we traverse.
2468 static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
2469 _current = _items.front.save;
2470 else
2471 _current = _items.front;
2472 }
2473 }
2474 static if (isInfinite!RoR)
2475 {
2476 enum bool empty = false;
2477 }
2478 else
2479 {
emptyResult2480 @property auto empty()
2481 {
2482 return _items.empty;
2483 }
2484 }
frontResult2485 @property auto ref front()
2486 {
2487 assert(!empty, "Attempting to fetch the front of an empty joiner.");
2488 return _current.front;
2489 }
popFrontResult2490 void popFront()
2491 {
2492 assert(!_current.empty, "Attempting to popFront an empty joiner.");
2493 _current.popFront();
2494 if (_current.empty)
2495 {
2496 assert(!_items.empty);
2497 _items.popFront();
2498 mixin(prepare);
2499 }
2500 }
2501 static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
2502 {
saveResult2503 @property auto save()
2504 {
2505 return Result(_items.save, _current.save);
2506 }
2507 }
2508
2509 static if (hasAssignableElements!(ElementType!RoR))
2510 {
2511 @property void front(ElementType!(ElementType!RoR) element)
2512 {
2513 assert(!empty, "Attempting to assign to front of an empty joiner.");
2514 _current.front = element;
2515 }
2516
2517 @property void front(ref ElementType!(ElementType!RoR) element)
2518 {
2519 assert(!empty, "Attempting to assign to front of an empty joiner.");
2520 _current.front = element;
2521 }
2522 }
2523 }
2524 return Result(r);
2525 }
2526
2527 @safe unittest
2528 {
2529 import std.algorithm.comparison : equal;
2530 import std.range.interfaces : inputRangeObject;
2531 import std.range : repeat;
2532
2533 static assert(isInputRange!(typeof(joiner([""]))));
2534 static assert(isForwardRange!(typeof(joiner([""]))));
2535 assert(equal(joiner([""]), ""));
2536 assert(equal(joiner(["", ""]), ""));
2537 assert(equal(joiner(["", "abc"]), "abc"));
2538 assert(equal(joiner(["abc", ""]), "abc"));
2539 assert(equal(joiner(["abc", "def"]), "abcdef"));
2540 assert(equal(joiner(["Mary", "has", "a", "little", "lamb"]),
2541 "Maryhasalittlelamb"));
2542 assert(equal(joiner(repeat("abc", 3)), "abcabcabc"));
2543
2544 // joiner allows in-place mutation!
2545 auto a = [ [1, 2, 3], [42, 43] ];
2546 auto j = joiner(a);
2547 j.front = 44;
2548 assert(a == [ [44, 2, 3], [42, 43] ]);
2549 assert(equal(j, [44, 2, 3, 42, 43]));
2550 }
2551
2552
2553 @system unittest
2554 {
2555 import std.algorithm.comparison : equal;
2556 import std.range.interfaces : inputRangeObject;
2557
2558 // bugzilla 8240
2559 assert(equal(joiner([inputRangeObject("")]), ""));
2560
2561 // issue 8792
2562 auto b = [[1], [2], [3]];
2563 auto jb = joiner(b);
2564 auto js = jb.save;
2565 assert(equal(jb, js));
2566
2567 auto js2 = jb.save;
2568 jb.popFront();
2569 assert(!equal(jb, js));
2570 assert(equal(js2, js));
2571 js.popFront();
2572 assert(equal(jb, js));
2573 assert(!equal(js2, js));
2574 }
2575
2576 @safe unittest
2577 {
2578 import std.algorithm.comparison : equal;
2579
2580 struct TransientRange
2581 {
2582 @safe:
2583 int[] _buf;
2584 int[][] _values;
thisTransientRange2585 this(int[][] values)
2586 {
2587 _values = values;
2588 _buf = new int[128];
2589 }
emptyTransientRange2590 @property bool empty()
2591 {
2592 return _values.length == 0;
2593 }
frontTransientRange2594 @property auto front()
2595 {
2596 foreach (i; 0 .. _values.front.length)
2597 {
2598 _buf[i] = _values[0][i];
2599 }
2600 return _buf[0 .. _values.front.length];
2601 }
popFrontTransientRange2602 void popFront()
2603 {
2604 _values = _values[1 .. $];
2605 }
2606 }
2607
2608 auto rr = TransientRange([[1,2], [3,4,5], [], [6,7]]);
2609
2610 // Can't use array() or equal() directly because they fail with transient
2611 // .front.
2612 int[] result;
2613 foreach (c; rr.joiner())
2614 {
2615 result ~= c;
2616 }
2617
2618 assert(equal(result, [1,2,3,4,5,6,7]));
2619 }
2620
2621 @safe unittest
2622 {
2623 import std.algorithm.comparison : equal;
2624 import std.algorithm.internal : algoFormat;
2625
2626 struct TransientRange
2627 {
2628 @safe:
2629 dchar[] _buf;
2630 dstring[] _values;
thisTransientRange2631 this(dstring[] values)
2632 {
2633 _buf.length = 128;
2634 _values = values;
2635 }
emptyTransientRange2636 @property bool empty()
2637 {
2638 return _values.length == 0;
2639 }
frontTransientRange2640 @property auto front()
2641 {
2642 foreach (i; 0 .. _values.front.length)
2643 {
2644 _buf[i] = _values[0][i];
2645 }
2646 return _buf[0 .. _values.front.length];
2647 }
popFrontTransientRange2648 void popFront()
2649 {
2650 _values = _values[1 .. $];
2651 }
2652 }
2653
2654 auto rr = TransientRange(["abc"d, "12"d, "def"d, "34"d]);
2655
2656 // Can't use array() or equal() directly because they fail with transient
2657 // .front.
2658 dchar[] result;
2659 foreach (c; rr.joiner())
2660 {
2661 result ~= c;
2662 }
2663
2664 import std.conv : to;
2665 assert(equal(result, "abc12def34"d),
2666 //Convert to string for assert's message
2667 to!string("Unexpected result: '%s'"d.algoFormat(result)));
2668 }
2669
2670 // Issue 8061
2671 @system unittest
2672 {
2673 import std.conv : to;
2674 import std.range.interfaces;
2675
2676 auto r = joiner([inputRangeObject("ab"), inputRangeObject("cd")]);
2677 assert(isForwardRange!(typeof(r)));
2678
2679 auto str = to!string(r);
2680 assert(str == "abcd");
2681 }
2682
2683 @safe unittest
2684 {
2685 import std.range : repeat;
2686
2687 class AssignableRange
2688 {
2689 @safe:
2690 int element;
front()2691 @property int front()
2692 {
2693 return element;
2694 }
2695
2696 enum empty = false;
2697
popFront()2698 void popFront()
2699 {
2700 }
2701
front(int newValue)2702 @property void front(int newValue)
2703 {
2704 element = newValue;
2705 }
2706 }
2707
2708 static assert(isInputRange!AssignableRange);
2709 static assert(is(ElementType!AssignableRange == int));
2710 static assert(hasAssignableElements!AssignableRange);
2711 static assert(!hasLvalueElements!AssignableRange);
2712
2713 auto range = new AssignableRange();
2714 assert(range.element == 0);
2715
2716 auto joined = joiner(repeat(range));
2717 joined.front = 5;
2718 assert(range.element == 5);
2719 assert(joined.front == 5);
2720
2721 joined.popFront;
2722 int byRef = 7;
2723 joined.front = byRef;
2724 assert(range.element == byRef);
2725 assert(joined.front == byRef);
2726 }
2727
2728 /++
2729 Implements the homonym function (also known as $(D accumulate), $(D
2730 compress), $(D inject), or $(D foldl)) present in various programming
2731 languages of functional flavor. There is also $(LREF fold) which does
2732 the same thing but with the opposite parameter order.
2733 The call $(D reduce!(fun)(seed, range)) first assigns $(D seed) to
2734 an internal variable $(D result), also called the accumulator.
2735 Then, for each element $(D x) in $(D range), $(D result = fun(result, x))
2736 gets evaluated. Finally, $(D result) is returned.
2737 The one-argument version $(D reduce!(fun)(range))
2738 works similarly, but it uses the first element of the range as the
2739 seed (the range must be non-empty).
2740
2741 Returns:
2742 the accumulated $(D result)
2743
2744 Params:
2745 fun = one or more functions
2746
2747 See_Also:
2748 $(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function))
2749
2750 $(LREF fold) is functionally equivalent to $(LREF reduce) with the argument order reversed,
2751 and without the need to use $(LREF tuple) for multiple seeds. This makes it easier
2752 to use in UFCS chains.
2753
2754 $(LREF sum) is similar to $(D reduce!((a, b) => a + b)) that offers
2755 pairwise summing of floating point numbers.
2756 +/
2757 template reduce(fun...)
2758 if (fun.length >= 1)
2759 {
2760 import std.meta : staticMap;
2761
2762 alias binfuns = staticMap!(binaryFun, fun);
2763 static if (fun.length > 1)
2764 import std.typecons : tuple, isTuple;
2765
2766 /++
2767 No-seed version. The first element of $(D r) is used as the seed's value.
2768
2769 For each function $(D f) in $(D fun), the corresponding
2770 seed type $(D S) is $(D Unqual!(typeof(f(e, e)))), where $(D e) is an
2771 element of $(D r): $(D ElementType!R) for ranges,
2772 and $(D ForeachType!R) otherwise.
2773
2774 Once S has been determined, then $(D S s = e;) and $(D s = f(s, e);)
2775 must both be legal.
2776
2777 If $(D r) is empty, an $(D Exception) is thrown.
2778
2779 Params:
2780 r = an iterable value as defined by $(D isIterable)
2781
2782 Returns:
2783 the final result of the accumulator applied to the iterable
2784 +/
2785 auto reduce(R)(R r)
2786 if (isIterable!R)
2787 {
2788 import std.exception : enforce;
2789 alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R);
2790 alias Args = staticMap!(ReduceSeedType!E, binfuns);
2791
2792 static if (isInputRange!R)
2793 {
2794 enforce(!r.empty, "Cannot reduce an empty input range w/o an explicit seed value.");
2795 Args result = r.front;
2796 r.popFront();
2797 return reduceImpl!false(r, result);
2798 }
2799 else
2800 {
2801 auto result = Args.init;
2802 return reduceImpl!true(r, result);
2803 }
2804 }
2805
2806 /++
2807 Seed version. The seed should be a single value if $(D fun) is a
2808 single function. If $(D fun) is multiple functions, then $(D seed)
2809 should be a $(REF Tuple, std,typecons), with one field per function in $(D f).
2810
2811 For convenience, if the seed is const, or has qualified fields, then
2812 $(D reduce) will operate on an unqualified copy. If this happens
2813 then the returned type will not perfectly match $(D S).
2814
2815 Use $(D fold) instead of $(D reduce) to use the seed version in a UFCS chain.
2816
2817 Params:
2818 seed = the initial value of the accumulator
2819 r = an iterable value as defined by $(D isIterable)
2820
2821 Returns:
2822 the final result of the accumulator applied to the iterable
2823 +/
2824 auto reduce(S, R)(S seed, R r)
2825 if (isIterable!R)
2826 {
2827 static if (fun.length == 1)
2828 return reducePreImpl(r, seed);
2829 else
2830 {
2831 import std.algorithm.internal : algoFormat;
2832 static assert(isTuple!S, algoFormat("Seed %s should be a Tuple", S.stringof));
2833 return reducePreImpl(r, seed.expand);
2834 }
2835 }
2836
reducePreImpl(R,Args...)2837 private auto reducePreImpl(R, Args...)(R r, ref Args args)
2838 {
2839 alias Result = staticMap!(Unqual, Args);
2840 static if (is(Result == Args))
2841 alias result = args;
2842 else
2843 Result result = args;
2844 return reduceImpl!false(r, result);
2845 }
2846
2847 private auto reduceImpl(bool mustInitialize, R, Args...)(R r, ref Args args)
2848 if (isIterable!R)
2849 {
2850 import std.algorithm.internal : algoFormat;
2851 static assert(Args.length == fun.length,
2852 algoFormat("Seed %s does not have the correct amount of fields (should be %s)", Args.stringof, fun.length));
2853 alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R);
2854
2855 static if (mustInitialize) bool initialized = false;
2856 foreach (/+auto ref+/ E e; r) // @@@4707@@@
2857 {
2858 foreach (i, f; binfuns)
2859 {
2860 static assert(!is(typeof(f(args[i], e))) || is(typeof(args[i] = f(args[i], e))),
2861 algoFormat(
2862 "Incompatible function/seed/element: %s/%s/%s",
2863 fullyQualifiedName!f,
2864 Args[i].stringof,
2865 E.stringof
2866 )
2867 );
2868 }
2869
2870 static if (mustInitialize) if (initialized == false)
2871 {
2872 import std.conv : emplaceRef;
2873 foreach (i, f; binfuns)
2874 emplaceRef!(Args[i])(args[i], e);
2875 initialized = true;
2876 continue;
2877 }
2878
2879 foreach (i, f; binfuns)
2880 args[i] = f(args[i], e);
2881 }
2882 static if (mustInitialize)
2883 if (!initialized)
2884 throw new Exception("Cannot reduce an empty iterable w/o an explicit seed value.");
2885
2886 static if (Args.length == 1)
2887 return args[0];
2888 else
2889 return tuple(args);
2890 }
2891 }
2892
2893 /**
2894 Many aggregate range operations turn out to be solved with $(D reduce)
2895 quickly and easily. The example below illustrates $(D reduce)'s
2896 remarkable power and flexibility.
2897 */
2898 @safe unittest
2899 {
2900 import std.algorithm.comparison : max, min;
2901 import std.math : approxEqual;
2902 import std.range;
2903
2904 int[] arr = [ 1, 2, 3, 4, 5 ];
2905 // Sum all elements
2906 auto sum = reduce!((a,b) => a + b)(0, arr);
2907 assert(sum == 15);
2908
2909 // Sum again, using a string predicate with "a" and "b"
2910 sum = reduce!"a + b"(0, arr);
2911 assert(sum == 15);
2912
2913 // Compute the maximum of all elements
2914 auto largest = reduce!(max)(arr);
2915 assert(largest == 5);
2916
2917 // Max again, but with Uniform Function Call Syntax (UFCS)
2918 largest = arr.reduce!(max);
2919 assert(largest == 5);
2920
2921 // Compute the number of odd elements
2922 auto odds = reduce!((a,b) => a + (b & 1))(0, arr);
2923 assert(odds == 3);
2924
2925 // Compute the sum of squares
2926 auto ssquares = reduce!((a,b) => a + b * b)(0, arr);
2927 assert(ssquares == 55);
2928
2929 // Chain multiple ranges into seed
2930 int[] a = [ 3, 4 ];
2931 int[] b = [ 100 ];
2932 auto r = reduce!("a + b")(chain(a, b));
2933 assert(r == 107);
2934
2935 // Mixing convertible types is fair game, too
2936 double[] c = [ 2.5, 3.0 ];
2937 auto r1 = reduce!("a + b")(chain(a, b, c));
2938 assert(approxEqual(r1, 112.5));
2939
2940 // To minimize nesting of parentheses, Uniform Function Call Syntax can be used
2941 auto r2 = chain(a, b, c).reduce!("a + b");
2942 assert(approxEqual(r2, 112.5));
2943 }
2944
2945 /**
2946 Sometimes it is very useful to compute multiple aggregates in one pass.
2947 One advantage is that the computation is faster because the looping overhead
2948 is shared. That's why $(D reduce) accepts multiple functions.
2949 If two or more functions are passed, $(D reduce) returns a
2950 $(REF Tuple, std,typecons) object with one member per passed-in function.
2951 The number of seeds must be correspondingly increased.
2952 */
2953 @safe unittest
2954 {
2955 import std.algorithm.comparison : max, min;
2956 import std.math : approxEqual, sqrt;
2957 import std.typecons : tuple, Tuple;
2958
2959 double[] a = [ 3.0, 4, 7, 11, 3, 2, 5 ];
2960 // Compute minimum and maximum in one pass
2961 auto r = reduce!(min, max)(a);
2962 // The type of r is Tuple!(int, int)
2963 assert(approxEqual(r[0], 2)); // minimum
2964 assert(approxEqual(r[1], 11)); // maximum
2965
2966 // Compute sum and sum of squares in one pass
2967 r = reduce!("a + b", "a + b * b")(tuple(0.0, 0.0), a);
2968 assert(approxEqual(r[0], 35)); // sum
2969 assert(approxEqual(r[1], 233)); // sum of squares
2970 // Compute average and standard deviation from the above
2971 auto avg = r[0] / a.length;
2972 assert(avg == 5);
2973 auto stdev = sqrt(r[1] / a.length - avg * avg);
2974 assert(cast(int) stdev == 2);
2975 }
2976
2977 @safe unittest
2978 {
2979 import std.algorithm.comparison : max, min;
2980 import std.range : chain;
2981 import std.typecons : tuple, Tuple;
2982
2983 double[] a = [ 3, 4 ];
2984 auto r = reduce!("a + b")(0.0, a);
2985 assert(r == 7);
2986 r = reduce!("a + b")(a);
2987 assert(r == 7);
2988 r = reduce!(min)(a);
2989 assert(r == 3);
2990 double[] b = [ 100 ];
2991 auto r1 = reduce!("a + b")(chain(a, b));
2992 assert(r1 == 107);
2993
2994 // two funs
2995 auto r2 = reduce!("a + b", "a - b")(tuple(0.0, 0.0), a);
2996 assert(r2[0] == 7 && r2[1] == -7);
2997 auto r3 = reduce!("a + b", "a - b")(a);
2998 assert(r3[0] == 7 && r3[1] == -1);
2999
3000 a = [ 1, 2, 3, 4, 5 ];
3001 // Stringize with commas
3002 string rep = reduce!("a ~ `, ` ~ to!(string)(b)")("", a);
3003 assert(rep[2 .. $] == "1, 2, 3, 4, 5", "["~rep[2 .. $]~"]");
3004 }
3005
3006 @system unittest
3007 {
3008 import std.algorithm.comparison : max, min;
3009 import std.exception : assertThrown;
3010 import std.range : iota;
3011 import std.typecons : tuple, Tuple;
3012
3013 // Test the opApply case.
3014 static struct OpApply
3015 {
3016 bool actEmpty;
3017
opApplyOpApply3018 int opApply(scope int delegate(ref int) dg)
3019 {
3020 int res;
3021 if (actEmpty) return res;
3022
3023 foreach (i; 0 .. 100)
3024 {
3025 res = dg(i);
3026 if (res) break;
3027 }
3028 return res;
3029 }
3030 }
3031
3032 OpApply oa;
3033 auto hundredSum = reduce!"a + b"(iota(100));
3034 assert(reduce!"a + b"(5, oa) == hundredSum + 5);
3035 assert(reduce!"a + b"(oa) == hundredSum);
3036 assert(reduce!("a + b", max)(oa) == tuple(hundredSum, 99));
3037 assert(reduce!("a + b", max)(tuple(5, 0), oa) == tuple(hundredSum + 5, 99));
3038
3039 // Test for throwing on empty range plus no seed.
3040 assertThrown(reduce!"a + b"([1, 2][0 .. 0]));
3041
3042 oa.actEmpty = true;
3043 assertThrown(reduce!"a + b"(oa));
3044 }
3045
3046 @safe unittest
3047 {
3048 const float a = 0.0;
3049 const float[] b = [ 1.2, 3, 3.3 ];
3050 float[] c = [ 1.2, 3, 3.3 ];
3051 auto r = reduce!"a + b"(a, b);
3052 r = reduce!"a + b"(a, c);
3053 assert(r == 7.5);
3054 }
3055
3056 @safe unittest
3057 {
3058 // Issue #10408 - Two-function reduce of a const array.
3059 import std.algorithm.comparison : max, min;
3060 import std.typecons : tuple, Tuple;
3061
3062 const numbers = [10, 30, 20];
3063 immutable m = reduce!(min)(numbers);
3064 assert(m == 10);
3065 immutable minmax = reduce!(min, max)(numbers);
3066 assert(minmax == tuple(10, 30));
3067 }
3068
3069 @safe unittest
3070 {
3071 //10709
3072 import std.typecons : tuple, Tuple;
3073
3074 enum foo = "a + 0.5 * b";
3075 auto r = [0, 1, 2, 3];
3076 auto r1 = reduce!foo(r);
3077 auto r2 = reduce!(foo, foo)(r);
3078 assert(r1 == 3);
3079 assert(r2 == tuple(3, 3));
3080 }
3081
3082 @system unittest
3083 {
3084 static struct OpApply
3085 {
opApplyOpApply3086 int opApply(int delegate(ref int) dg)
3087 {
3088 int[] a = [1, 2, 3];
3089
3090 int res = 0;
3091 foreach (ref e; a)
3092 {
3093 res = dg(e);
3094 if (res) break;
3095 }
3096 return res;
3097 }
3098 }
3099 //test CTFE and functions with context
fun(int a,int b)3100 int fun(int a, int b) @safe {return a + b + 1;}
foo()3101 auto foo()
3102 {
3103 import std.algorithm.comparison : max;
3104 import std.typecons : tuple, Tuple;
3105
3106 auto a = reduce!(fun)([1, 2, 3]);
3107 auto b = reduce!(fun, fun)([1, 2, 3]);
3108 auto c = reduce!(fun)(0, [1, 2, 3]);
3109 auto d = reduce!(fun, fun)(tuple(0, 0), [1, 2, 3]);
3110 auto e = reduce!(fun)(0, OpApply());
3111 auto f = reduce!(fun, fun)(tuple(0, 0), OpApply());
3112
3113 return max(a, b.expand, c, d.expand, e, f.expand);
3114 }
3115 auto a = foo();
3116 assert(a == 9);
3117 enum b = foo();
3118 assert(b == 9);
3119 }
3120
3121 @safe unittest
3122 {
3123 import std.algorithm.comparison : max, min;
3124 import std.typecons : tuple, Tuple;
3125
3126 //http://forum.dlang.org/post/oghtttkopzjshsuflelk@forum.dlang.org
3127 //Seed is tuple of const.
3128 static auto minmaxElement(alias F = min, alias G = max, R)(in R range)
3129 @safe pure nothrow
3130 if (isInputRange!R)
3131 {
3132 return reduce!(F, G)(tuple(ElementType!R.max,
3133 ElementType!R.min), range);
3134 }
3135 assert(minmaxElement([1, 2, 3]) == tuple(1, 3));
3136 }
3137
3138 @safe unittest //12569
3139 {
3140 import std.algorithm.comparison : max, min;
3141 import std.typecons : tuple;
3142 dchar c = 'a';
3143 reduce!(min, max)(tuple(c, c), "hello"); // OK
3144 static assert(!is(typeof(reduce!(min, max)(tuple(c), "hello"))));
3145 static assert(!is(typeof(reduce!(min, max)(tuple(c, c, c), "hello"))));
3146
3147
3148 //"Seed dchar should be a Tuple"
3149 static assert(!is(typeof(reduce!(min, max)(c, "hello"))));
3150 //"Seed (dchar) does not have the correct amount of fields (should be 2)"
3151 static assert(!is(typeof(reduce!(min, max)(tuple(c), "hello"))));
3152 //"Seed (dchar, dchar, dchar) does not have the correct amount of fields (should be 2)"
3153 static assert(!is(typeof(reduce!(min, max)(tuple(c, c, c), "hello"))));
3154 //"Incompatable function/seed/element: all(alias pred = "a")/int/dchar"
3155 static assert(!is(typeof(reduce!all(1, "hello"))));
3156 static assert(!is(typeof(reduce!(all, all)(tuple(1, 1), "hello"))));
3157 }
3158
3159 @safe unittest //13304
3160 {
3161 int[] data;
3162 static assert(is(typeof(reduce!((a, b) => a + b)(data))));
3163 assert(data.length == 0);
3164 }
3165
3166 //Helper for Reduce
ReduceSeedType(E)3167 private template ReduceSeedType(E)
3168 {
3169 static template ReduceSeedType(alias fun)
3170 {
3171 import std.algorithm.internal : algoFormat;
3172
3173 alias ReduceSeedType = Unqual!(typeof(fun(lvalueOf!E, lvalueOf!E)));
3174
3175 //Check the Seed type is useable.
3176 ReduceSeedType s = ReduceSeedType.init;
3177 static assert(is(typeof({ReduceSeedType s = lvalueOf!E;})) &&
3178 is(typeof(lvalueOf!ReduceSeedType = fun(lvalueOf!ReduceSeedType, lvalueOf!E))),
3179 algoFormat(
3180 "Unable to deduce an acceptable seed type for %s with element type %s.",
3181 fullyQualifiedName!fun,
3182 E.stringof
3183 )
3184 );
3185 }
3186 }
3187
3188
3189 /++
3190 Implements the homonym function (also known as $(D accumulate), $(D
3191 compress), $(D inject), or $(D foldl)) present in various programming
3192 languages of functional flavor. The call $(D fold!(fun)(range, seed))
3193 first assigns $(D seed) to an internal variable $(D result),
3194 also called the accumulator. Then, for each element $(D x) in $(D
3195 range), $(D result = fun(result, x)) gets evaluated. Finally, $(D
3196 result) is returned. The one-argument version $(D fold!(fun)(range))
3197 works similarly, but it uses the first element of the range as the
3198 seed (the range must be non-empty).
3199
3200 Returns:
3201 the accumulated $(D result)
3202
3203 See_Also:
3204 $(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function))
3205
3206 $(LREF sum) is similar to $(D fold!((a, b) => a + b)) that offers
3207 precise summing of floating point numbers.
3208
3209 This is functionally equivalent to $(LREF reduce) with the argument order reversed,
3210 and without the need to use $(LREF tuple) for multiple seeds.
3211 +/
3212 template fold(fun...)
3213 if (fun.length >= 1)
3214 {
fold(R,S...)3215 auto fold(R, S...)(R r, S seed)
3216 {
3217 static if (S.length < 2)
3218 {
3219 return reduce!fun(seed, r);
3220 }
3221 else
3222 {
3223 import std.typecons : tuple;
3224 return reduce!fun(tuple(seed), r);
3225 }
3226 }
3227 }
3228
3229 ///
3230 @safe pure unittest
3231 {
3232 immutable arr = [1, 2, 3, 4, 5];
3233
3234 // Sum all elements
3235 assert(arr.fold!((a, b) => a + b) == 15);
3236
3237 // Sum all elements with explicit seed
3238 assert(arr.fold!((a, b) => a + b)(6) == 21);
3239
3240 import std.algorithm.comparison : min, max;
3241 import std.typecons : tuple;
3242
3243 // Compute minimum and maximum at the same time
3244 assert(arr.fold!(min, max) == tuple(1, 5));
3245
3246 // Compute minimum and maximum at the same time with seeds
3247 assert(arr.fold!(min, max)(0, 7) == tuple(0, 7));
3248
3249 // Can be used in a UFCS chain
3250 assert(arr.map!(a => a + 1).fold!((a, b) => a + b) == 20);
3251
3252 // Return the last element of any range
3253 assert(arr.fold!((a, b) => b) == 5);
3254 }
3255
3256 @safe @nogc pure nothrow unittest
3257 {
3258 int[1] arr;
3259 static assert(!is(typeof(arr.fold!())));
3260 static assert(!is(typeof(arr.fold!(a => a))));
3261 static assert(is(typeof(arr.fold!((a, b) => a))));
3262 static assert(is(typeof(arr.fold!((a, b) => a)(1))));
3263 assert(arr.length == 1);
3264 }
3265
3266 /++
3267 Similar to `fold`, but returns a range containing the successive reduced values.
3268 The call $(D cumulativeFold!(fun)(range, seed)) first assigns `seed` to an
3269 internal variable `result`, also called the accumulator.
3270 The returned range contains the values $(D result = fun(result, x)) lazily
3271 evaluated for each element `x` in `range`. Finally, the last element has the
3272 same value as $(D fold!(fun)(seed, range)).
3273 The one-argument version $(D cumulativeFold!(fun)(range)) works similarly, but
3274 it returns the first element unchanged and uses it as seed for the next
3275 elements.
3276 This function is also known as
3277 $(HTTP en.cppreference.com/w/cpp/algorithm/partial_sum, partial_sum),
3278 $(HTTP docs.python.org/3/library/itertools.html#itertools.accumulate, accumulate),
3279 $(HTTP hackage.haskell.org/package/base-4.8.2.0/docs/Prelude.html#v:scanl, scan),
3280 $(HTTP mathworld.wolfram.com/CumulativeSum.html, Cumulative Sum).
3281
3282 Params:
3283 fun = one or more functions to use as fold operation
3284
3285 Returns:
3286 The function returns a range containing the consecutive reduced values. If
3287 there is more than one `fun`, the element type will be $(REF Tuple,
3288 std,typecons) containing one element for each `fun`.
3289
3290 See_Also:
3291 $(HTTP en.wikipedia.org/wiki/Prefix_sum, Prefix Sum)
3292 +/
3293 template cumulativeFold(fun...)
3294 if (fun.length >= 1)
3295 {
3296 import std.meta : staticMap;
3297 private alias binfuns = staticMap!(binaryFun, fun);
3298
3299 /++
3300 No-seed version. The first element of `r` is used as the seed's value.
3301 For each function `f` in `fun`, the corresponding seed type `S` is
3302 $(D Unqual!(typeof(f(e, e)))), where `e` is an element of `r`:
3303 `ElementType!R`.
3304 Once `S` has been determined, then $(D S s = e;) and $(D s = f(s, e);) must
3305 both be legal.
3306
3307 Params:
3308 range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
3309 Returns:
3310 a range containing the consecutive reduced values.
3311 +/
3312 auto cumulativeFold(R)(R range)
3313 if (isInputRange!(Unqual!R))
3314 {
3315 return cumulativeFoldImpl(range);
3316 }
3317
3318 /++
3319 Seed version. The seed should be a single value if `fun` is a single
3320 function. If `fun` is multiple functions, then `seed` should be a
3321 $(REF Tuple, std,typecons), with one field per function in `f`.
3322 For convenience, if the seed is `const`, or has qualified fields, then
3323 `cumulativeFold` will operate on an unqualified copy. If this happens
3324 then the returned type will not perfectly match `S`.
3325
3326 Params:
3327 range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
3328 seed = the initial value of the accumulator
3329 Returns:
3330 a range containing the consecutive reduced values.
3331 +/
3332 auto cumulativeFold(R, S)(R range, S seed)
3333 if (isInputRange!(Unqual!R))
3334 {
3335 static if (fun.length == 1)
3336 return cumulativeFoldImpl(range, seed);
3337 else
3338 return cumulativeFoldImpl(range, seed.expand);
3339 }
3340
cumulativeFoldImpl(R,Args...)3341 private auto cumulativeFoldImpl(R, Args...)(R range, ref Args args)
3342 {
3343 import std.algorithm.internal : algoFormat;
3344
3345 static assert(Args.length == 0 || Args.length == fun.length,
3346 algoFormat("Seed %s does not have the correct amount of fields (should be %s)",
3347 Args.stringof, fun.length));
3348
3349 static if (args.length)
3350 alias State = staticMap!(Unqual, Args);
3351 else
3352 alias State = staticMap!(ReduceSeedType!(ElementType!R), binfuns);
3353
3354 foreach (i, f; binfuns)
3355 {
3356 static assert(!__traits(compiles, f(args[i], e)) || __traits(compiles,
3357 { args[i] = f(args[i], e); }()),
3358 algoFormat("Incompatible function/seed/element: %s/%s/%s",
3359 fullyQualifiedName!f, Args[i].stringof, E.stringof));
3360 }
3361
3362 static struct Result
3363 {
3364 private:
3365 R source;
3366 State state;
3367
3368 this(R range, ref Args args)
3369 {
3370 source = range;
3371 if (source.empty)
3372 return;
3373
3374 foreach (i, f; binfuns)
3375 {
3376 static if (args.length)
3377 state[i] = f(args[i], source.front);
3378 else
3379 state[i] = source.front;
3380 }
3381 }
3382
3383 public:
3384 @property bool empty()
3385 {
3386 return source.empty;
3387 }
3388
3389 @property auto front()
3390 {
3391 assert(!empty, "Attempting to fetch the front of an empty cumulativeFold.");
3392 static if (fun.length > 1)
3393 {
3394 import std.typecons : tuple;
3395 return tuple(state);
3396 }
3397 else
3398 {
3399 return state[0];
3400 }
3401 }
3402
3403 void popFront()
3404 {
3405 assert(!empty, "Attempting to popFront an empty cumulativeFold.");
3406 source.popFront;
3407
3408 if (source.empty)
3409 return;
3410
3411 foreach (i, f; binfuns)
3412 state[i] = f(state[i], source.front);
3413 }
3414
3415 static if (isForwardRange!R)
3416 {
3417 @property auto save()
3418 {
3419 auto result = this;
3420 result.source = source.save;
3421 return result;
3422 }
3423 }
3424
3425 static if (hasLength!R)
3426 {
3427 @property size_t length()
3428 {
3429 return source.length;
3430 }
3431 }
3432 }
3433
3434 return Result(range, args);
3435 }
3436 }
3437
3438 ///
3439 @safe unittest
3440 {
3441 import std.algorithm.comparison : max, min;
3442 import std.array : array;
3443 import std.math : approxEqual;
3444 import std.range : chain;
3445
3446 int[] arr = [1, 2, 3, 4, 5];
3447 // Partial sum of all elements
3448 auto sum = cumulativeFold!((a, b) => a + b)(arr, 0);
3449 assert(sum.array == [1, 3, 6, 10, 15]);
3450
3451 // Partial sum again, using a string predicate with "a" and "b"
3452 auto sum2 = cumulativeFold!"a + b"(arr, 0);
3453 assert(sum2.array == [1, 3, 6, 10, 15]);
3454
3455 // Compute the partial maximum of all elements
3456 auto largest = cumulativeFold!max(arr);
3457 assert(largest.array == [1, 2, 3, 4, 5]);
3458
3459 // Partial max again, but with Uniform Function Call Syntax (UFCS)
3460 largest = arr.cumulativeFold!max;
3461 assert(largest.array == [1, 2, 3, 4, 5]);
3462
3463 // Partial count of odd elements
3464 auto odds = arr.cumulativeFold!((a, b) => a + (b & 1))(0);
3465 assert(odds.array == [1, 1, 2, 2, 3]);
3466
3467 // Compute the partial sum of squares
3468 auto ssquares = arr.cumulativeFold!((a, b) => a + b * b)(0);
3469 assert(ssquares.array == [1, 5, 14, 30, 55]);
3470
3471 // Chain multiple ranges into seed
3472 int[] a = [3, 4];
3473 int[] b = [100];
3474 auto r = cumulativeFold!"a + b"(chain(a, b));
3475 assert(r.array == [3, 7, 107]);
3476
3477 // Mixing convertible types is fair game, too
3478 double[] c = [2.5, 3.0];
3479 auto r1 = cumulativeFold!"a + b"(chain(a, b, c));
3480 assert(approxEqual(r1, [3, 7, 107, 109.5, 112.5]));
3481
3482 // To minimize nesting of parentheses, Uniform Function Call Syntax can be used
3483 auto r2 = chain(a, b, c).cumulativeFold!"a + b";
3484 assert(approxEqual(r2, [3, 7, 107, 109.5, 112.5]));
3485 }
3486
3487 /**
3488 Sometimes it is very useful to compute multiple aggregates in one pass.
3489 One advantage is that the computation is faster because the looping overhead
3490 is shared. That's why `cumulativeFold` accepts multiple functions.
3491 If two or more functions are passed, `cumulativeFold` returns a $(REF Tuple,
3492 std,typecons) object with one member per passed-in function.
3493 The number of seeds must be correspondingly increased.
3494 */
3495 @safe unittest
3496 {
3497 import std.algorithm.comparison : max, min;
3498 import std.algorithm.iteration : map;
3499 import std.math : approxEqual;
3500 import std.typecons : tuple;
3501
3502 double[] a = [3.0, 4, 7, 11, 3, 2, 5];
3503 // Compute minimum and maximum in one pass
3504 auto r = a.cumulativeFold!(min, max);
3505 // The type of r is Tuple!(int, int)
3506 assert(approxEqual(r.map!"a[0]", [3, 3, 3, 3, 3, 2, 2])); // minimum
3507 assert(approxEqual(r.map!"a[1]", [3, 4, 7, 11, 11, 11, 11])); // maximum
3508
3509 // Compute sum and sum of squares in one pass
3510 auto r2 = a.cumulativeFold!("a + b", "a + b * b")(tuple(0.0, 0.0));
3511 assert(approxEqual(r2.map!"a[0]", [3, 7, 14, 25, 28, 30, 35])); // sum
3512 assert(approxEqual(r2.map!"a[1]", [9, 25, 74, 195, 204, 208, 233])); // sum of squares
3513 }
3514
3515 @safe unittest
3516 {
3517 import std.algorithm.comparison : equal, max, min;
3518 import std.conv : to;
3519 import std.range : chain;
3520 import std.typecons : tuple;
3521
3522 double[] a = [3, 4];
3523 auto r = a.cumulativeFold!("a + b")(0.0);
3524 assert(r.equal([3, 7]));
3525 auto r2 = cumulativeFold!("a + b")(a);
3526 assert(r2.equal([3, 7]));
3527 auto r3 = cumulativeFold!(min)(a);
3528 assert(r3.equal([3, 3]));
3529 double[] b = [100];
3530 auto r4 = cumulativeFold!("a + b")(chain(a, b));
3531 assert(r4.equal([3, 7, 107]));
3532
3533 // two funs
3534 auto r5 = cumulativeFold!("a + b", "a - b")(a, tuple(0.0, 0.0));
3535 assert(r5.equal([tuple(3, -3), tuple(7, -7)]));
3536 auto r6 = cumulativeFold!("a + b", "a - b")(a);
3537 assert(r6.equal([tuple(3, 3), tuple(7, -1)]));
3538
3539 a = [1, 2, 3, 4, 5];
3540 // Stringize with commas
3541 auto rep = cumulativeFold!("a ~ `, ` ~ to!string(b)")(a, "");
3542 assert(rep.map!"a[2 .. $]".equal(["1", "1, 2", "1, 2, 3", "1, 2, 3, 4", "1, 2, 3, 4, 5"]));
3543
3544 // Test for empty range
3545 a = [];
3546 assert(a.cumulativeFold!"a + b".empty);
3547 assert(a.cumulativeFold!"a + b"(2.0).empty);
3548 }
3549
3550 @safe unittest
3551 {
3552 import std.algorithm.comparison : max, min;
3553 import std.array : array;
3554 import std.math : approxEqual;
3555 import std.typecons : tuple;
3556
3557 const float a = 0.0;
3558 const float[] b = [1.2, 3, 3.3];
3559 float[] c = [1.2, 3, 3.3];
3560
3561 auto r = cumulativeFold!"a + b"(b, a);
3562 assert(approxEqual(r, [1.2, 4.2, 7.5]));
3563
3564 auto r2 = cumulativeFold!"a + b"(c, a);
3565 assert(approxEqual(r2, [1.2, 4.2, 7.5]));
3566
3567 const numbers = [10, 30, 20];
3568 enum m = numbers.cumulativeFold!(min).array;
3569 assert(m == [10, 10, 10]);
3570 enum minmax = numbers.cumulativeFold!(min, max).array;
3571 assert(minmax == [tuple(10, 10), tuple(10, 30), tuple(10, 30)]);
3572 }
3573
3574 @safe unittest
3575 {
3576 import std.math : approxEqual;
3577 import std.typecons : tuple;
3578
3579 enum foo = "a + 0.5 * b";
3580 auto r = [0, 1, 2, 3];
3581 auto r1 = r.cumulativeFold!foo;
3582 auto r2 = r.cumulativeFold!(foo, foo);
3583 assert(approxEqual(r1, [0, 0.5, 1.5, 3]));
3584 assert(approxEqual(r2.map!"a[0]", [0, 0.5, 1.5, 3]));
3585 assert(approxEqual(r2.map!"a[1]", [0, 0.5, 1.5, 3]));
3586 }
3587
3588 @safe unittest
3589 {
3590 import std.algorithm.comparison : equal, max, min;
3591 import std.array : array;
3592 import std.typecons : tuple;
3593
3594 //Seed is tuple of const.
3595 static auto minmaxElement(alias F = min, alias G = max, R)(in R range)
3596 @safe pure nothrow
3597 if (isInputRange!R)
3598 {
3599 return range.cumulativeFold!(F, G)(tuple(ElementType!R.max, ElementType!R.min));
3600 }
3601
3602 assert(minmaxElement([1, 2, 3]).equal([tuple(1, 1), tuple(1, 2), tuple(1, 3)]));
3603 }
3604
3605 @safe unittest //12569
3606 {
3607 import std.algorithm.comparison : equal, max, min;
3608 import std.typecons : tuple;
3609
3610 dchar c = 'a';
3611
3612 assert(cumulativeFold!(min, max)("hello", tuple(c, c)).equal([tuple('a', 'h'),
3613 tuple('a', 'h'), tuple('a', 'l'), tuple('a', 'l'), tuple('a', 'o')]));
3614 static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c))));
3615 static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c, c, c))));
3616
3617 //"Seed dchar should be a Tuple"
3618 static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", c)));
3619 //"Seed (dchar) does not have the correct amount of fields (should be 2)"
3620 static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c))));
3621 //"Seed (dchar, dchar, dchar) does not have the correct amount of fields (should be 2)"
3622 static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c, c, c))));
3623 //"Incompatable function/seed/element: all(alias pred = "a")/int/dchar"
3624 static assert(!__traits(compiles, cumulativeFold!all("hello", 1)));
3625 static assert(!__traits(compiles, cumulativeFold!(all, all)("hello", tuple(1, 1))));
3626 }
3627
3628 @safe unittest //13304
3629 {
3630 int[] data;
3631 assert(data.cumulativeFold!((a, b) => a + b).empty);
3632 }
3633
3634 @safe unittest
3635 {
3636 import std.algorithm.comparison : equal;
3637 import std.internal.test.dummyrange : AllDummyRanges, propagatesLength,
3638 propagatesRangeType, RangeType;
3639
foreach(DummyType;AllDummyRanges)3640 foreach (DummyType; AllDummyRanges)
3641 {
3642 DummyType d;
3643 auto m = d.cumulativeFold!"a * b";
3644
3645 static assert(propagatesLength!(typeof(m), DummyType));
3646 static if (DummyType.rt <= RangeType.Forward)
3647 static assert(propagatesRangeType!(typeof(m), DummyType));
3648
3649 assert(m.equal([1, 2, 6, 24, 120, 720, 5040, 40_320, 362_880, 3_628_800]));
3650 }
3651 }
3652
3653 // splitter
3654 /**
3655 Lazily splits a range using an element as a separator. This can be used with
3656 any narrow string type or sliceable range type, but is most popular with string
3657 types.
3658
3659 Two adjacent separators are considered to surround an empty element in
3660 the split range. Use $(D filter!(a => !a.empty)) on the result to compress
3661 empty elements.
3662
3663 The predicate is passed to $(REF binaryFun, std,functional), and can either accept
3664 a string, or any callable that can be executed via $(D pred(element, s)).
3665
3666 If the empty range is given, the result is a range with one empty
3667 element. If a range with one separator is given, the result is a range
3668 with two empty elements.
3669
3670 If splitting a string on whitespace and token compression is desired,
3671 consider using $(D splitter) without specifying a separator (see fourth overload
3672 below).
3673
3674 Params:
3675 pred = The predicate for comparing each element with the separator,
3676 defaulting to $(D "a == b").
3677 r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be
3678 split. Must support slicing and $(D .length).
3679 s = The element to be treated as the separator between range segments to be
3680 split.
3681
3682 Constraints:
3683 The predicate $(D pred) needs to accept an element of $(D r) and the
3684 separator $(D s).
3685
3686 Returns:
3687 An input range of the subranges of elements between separators. If $(D r)
3688 is a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
3689 or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives),
3690 the returned range will be likewise.
3691
3692 See_Also:
3693 $(REF _splitter, std,regex) for a version that splits using a regular
3694 expression defined separator.
3695 */
3696 auto splitter(alias pred = "a == b", Range, Separator)(Range r, Separator s)
3697 if (is(typeof(binaryFun!pred(r.front, s)) : bool)
3698 && ((hasSlicing!Range && hasLength!Range) || isNarrowString!Range))
3699 {
3700 import std.algorithm.searching : find;
3701 import std.conv : unsigned;
3702
3703 static struct Result
3704 {
3705 private:
3706 Range _input;
3707 Separator _separator;
3708 // Do we need hasLength!Range? popFront uses _input.length...
3709 enum size_t _unComputed = size_t.max - 1, _atEnd = size_t.max;
3710 size_t _frontLength = _unComputed;
3711 size_t _backLength = _unComputed;
3712
3713 static if (isNarrowString!Range)
3714 {
3715 size_t _separatorLength;
3716 }
3717 else
3718 {
3719 enum _separatorLength = 1;
3720 }
3721
3722 static if (isBidirectionalRange!Range)
3723 {
lastIndexOfResult3724 static size_t lastIndexOf(Range haystack, Separator needle)
3725 {
3726 import std.range : retro;
3727 auto r = haystack.retro().find!pred(needle);
3728 return r.retro().length - 1;
3729 }
3730 }
3731
3732 public:
thisResult3733 this(Range input, Separator separator)
3734 {
3735 _input = input;
3736 _separator = separator;
3737
3738 static if (isNarrowString!Range)
3739 {
3740 import std.utf : codeLength;
3741
3742 _separatorLength = codeLength!(ElementEncodingType!Range)(separator);
3743 }
3744 if (_input.empty)
3745 _frontLength = _atEnd;
3746 }
3747
3748 static if (isInfinite!Range)
3749 {
3750 enum bool empty = false;
3751 }
3752 else
3753 {
emptyResult3754 @property bool empty()
3755 {
3756 return _frontLength == _atEnd;
3757 }
3758 }
3759
frontResult3760 @property Range front()
3761 {
3762 assert(!empty, "Attempting to fetch the front of an empty splitter.");
3763 if (_frontLength == _unComputed)
3764 {
3765 auto r = _input.find!pred(_separator);
3766 _frontLength = _input.length - r.length;
3767 }
3768 return _input[0 .. _frontLength];
3769 }
3770
popFrontResult3771 void popFront()
3772 {
3773 assert(!empty, "Attempting to popFront an empty splitter.");
3774 if (_frontLength == _unComputed)
3775 {
3776 front;
3777 }
3778 assert(_frontLength <= _input.length);
3779 if (_frontLength == _input.length)
3780 {
3781 // no more input and need to fetch => done
3782 _frontLength = _atEnd;
3783
3784 // Probably don't need this, but just for consistency:
3785 _backLength = _atEnd;
3786 }
3787 else
3788 {
3789 _input = _input[_frontLength + _separatorLength .. _input.length];
3790 _frontLength = _unComputed;
3791 }
3792 }
3793
3794 static if (isForwardRange!Range)
3795 {
typeofResult3796 @property typeof(this) save()
3797 {
3798 auto ret = this;
3799 ret._input = _input.save;
3800 return ret;
3801 }
3802 }
3803
3804 static if (isBidirectionalRange!Range)
3805 {
backResult3806 @property Range back()
3807 {
3808 assert(!empty, "Attempting to fetch the back of an empty splitter.");
3809 if (_backLength == _unComputed)
3810 {
3811 immutable lastIndex = lastIndexOf(_input, _separator);
3812 if (lastIndex == -1)
3813 {
3814 _backLength = _input.length;
3815 }
3816 else
3817 {
3818 _backLength = _input.length - lastIndex - 1;
3819 }
3820 }
3821 return _input[_input.length - _backLength .. _input.length];
3822 }
3823
popBackResult3824 void popBack()
3825 {
3826 assert(!empty, "Attempting to popBack an empty splitter.");
3827 if (_backLength == _unComputed)
3828 {
3829 // evaluate back to make sure it's computed
3830 back;
3831 }
3832 assert(_backLength <= _input.length);
3833 if (_backLength == _input.length)
3834 {
3835 // no more input and need to fetch => done
3836 _frontLength = _atEnd;
3837 _backLength = _atEnd;
3838 }
3839 else
3840 {
3841 _input = _input[0 .. _input.length - _backLength - _separatorLength];
3842 _backLength = _unComputed;
3843 }
3844 }
3845 }
3846 }
3847
3848 return Result(r, s);
3849 }
3850
3851 ///
3852 @safe unittest
3853 {
3854 import std.algorithm.comparison : equal;
3855
3856 assert(equal(splitter("hello world", ' '), [ "hello", "", "world" ]));
3857 int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
3858 int[][] w = [ [1, 2], [], [3], [4, 5], [] ];
3859 assert(equal(splitter(a, 0), w));
3860 a = [ 0 ];
3861 assert(equal(splitter(a, 0), [ (int[]).init, (int[]).init ]));
3862 a = [ 0, 1 ];
3863 assert(equal(splitter(a, 0), [ [], [1] ]));
3864 w = [ [0], [1], [2] ];
3865 assert(equal(splitter!"a.front == b"(w, 1), [ [[0]], [[2]] ]));
3866 }
3867
3868 @safe unittest
3869 {
3870 import std.algorithm;
3871 import std.array : array;
3872 import std.internal.test.dummyrange;
3873 import std.range : retro;
3874
3875 assert(equal(splitter("hello world", ' '), [ "hello", "", "world" ]));
3876 assert(equal(splitter("žlutoučkýřkůň", 'ř'), [ "žlutoučký", "kůň" ]));
3877 int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
3878 int[][] w = [ [1, 2], [], [3], [4, 5], [] ];
3879 static assert(isForwardRange!(typeof(splitter(a, 0))));
3880
3881 assert(equal(splitter(a, 0), w));
3882 a = null;
3883 assert(equal(splitter(a, 0), (int[][]).init));
3884 a = [ 0 ];
3885 assert(equal(splitter(a, 0), [ (int[]).init, (int[]).init ][]));
3886 a = [ 0, 1 ];
3887 assert(equal(splitter(a, 0), [ [], [1] ][]));
3888
3889 // Thoroughly exercise the bidirectional stuff.
3890 auto str = "abc abcd abcde ab abcdefg abcdefghij ab ac ar an at ada";
3891 assert(equal(
3892 retro(splitter(str, 'a')),
3893 retro(array(splitter(str, 'a')))
3894 ));
3895
3896 // Test interleaving front and back.
3897 auto split = splitter(str, 'a');
3898 assert(split.front == "");
3899 assert(split.back == "");
3900 split.popBack();
3901 assert(split.back == "d");
3902 split.popFront();
3903 assert(split.front == "bc ");
3904 assert(split.back == "d");
3905 split.popFront();
3906 split.popBack();
3907 assert(split.back == "t ");
3908 split.popBack();
3909 split.popBack();
3910 split.popFront();
3911 split.popFront();
3912 assert(split.front == "b ");
3913 assert(split.back == "r ");
3914
foreach(DummyType;AllDummyRanges)3915 foreach (DummyType; AllDummyRanges) { // Bug 4408
3916 static if (isRandomAccessRange!DummyType)
3917 {
3918 static assert(isBidirectionalRange!DummyType);
3919 DummyType d;
3920 auto s = splitter(d, 5);
3921 assert(equal(s.front, [1,2,3,4]));
3922 assert(equal(s.back, [6,7,8,9,10]));
3923
3924 auto s2 = splitter(d, [4, 5]);
3925 assert(equal(s2.front, [1,2,3]));
3926 }
3927 }
3928 }
3929 @safe unittest
3930 {
3931 import std.algorithm;
3932 import std.range;
3933 auto L = retro(iota(1L, 10L));
3934 auto s = splitter(L, 5L);
3935 assert(equal(s.front, [9L, 8L, 7L, 6L]));
3936 s.popFront();
3937 assert(equal(s.front, [4L, 3L, 2L, 1L]));
3938 s.popFront();
3939 assert(s.empty);
3940 }
3941
3942 /**
3943 Similar to the previous overload of $(D splitter), except this one uses another
3944 range as a separator. This can be used with any narrow string type or sliceable
3945 range type, but is most popular with string types. The predicate is passed to
3946 $(REF binaryFun, std,functional), and can either accept a string, or any callable
3947 that can be executed via $(D pred(r.front, s.front)).
3948
3949 Two adjacent separators are considered to surround an empty element in
3950 the split range. Use $(D filter!(a => !a.empty)) on the result to compress
3951 empty elements.
3952
3953 Params:
3954 pred = The predicate for comparing each element with the separator,
3955 defaulting to $(D "a == b").
3956 r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be
3957 split.
3958 s = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to
3959 be treated as the separator between segments of $(D r) to be split.
3960
3961 Constraints:
3962 The predicate $(D pred) needs to accept an element of $(D r) and an
3963 element of $(D s).
3964
3965 Returns:
3966 An input range of the subranges of elements between separators. If $(D r)
3967 is a forward range or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives),
3968 the returned range will be likewise.
3969
3970 See_Also: $(REF _splitter, std,regex) for a version that splits using a regular
3971 expression defined separator.
3972 */
3973 auto splitter(alias pred = "a == b", Range, Separator)(Range r, Separator s)
3974 if (is(typeof(binaryFun!pred(r.front, s.front)) : bool)
3975 && (hasSlicing!Range || isNarrowString!Range)
3976 && isForwardRange!Separator
3977 && (hasLength!Separator || isNarrowString!Separator))
3978 {
3979 import std.algorithm.searching : find;
3980 import std.conv : unsigned;
3981
3982 static struct Result
3983 {
3984 private:
3985 Range _input;
3986 Separator _separator;
3987 // _frontLength == size_t.max means empty
3988 size_t _frontLength = size_t.max;
3989 static if (isBidirectionalRange!Range)
3990 size_t _backLength = size_t.max;
3991
separatorLengthResult3992 @property auto separatorLength() { return _separator.length; }
3993
ensureFrontLengthResult3994 void ensureFrontLength()
3995 {
3996 if (_frontLength != _frontLength.max) return;
3997 assert(!_input.empty);
3998 // compute front length
3999 _frontLength = (_separator.empty) ? 1 :
4000 _input.length - find!pred(_input, _separator).length;
4001 static if (isBidirectionalRange!Range)
4002 if (_frontLength == _input.length) _backLength = _frontLength;
4003 }
4004
ensureBackLengthResult4005 void ensureBackLength()
4006 {
4007 static if (isBidirectionalRange!Range)
4008 if (_backLength != _backLength.max) return;
4009 assert(!_input.empty);
4010 // compute back length
4011 static if (isBidirectionalRange!Range && isBidirectionalRange!Separator)
4012 {
4013 import std.range : retro;
4014 _backLength = _input.length -
4015 find!pred(retro(_input), retro(_separator)).source.length;
4016 }
4017 }
4018
4019 public:
thisResult4020 this(Range input, Separator separator)
4021 {
4022 _input = input;
4023 _separator = separator;
4024 }
4025
frontResult4026 @property Range front()
4027 {
4028 assert(!empty, "Attempting to fetch the front of an empty splitter.");
4029 ensureFrontLength();
4030 return _input[0 .. _frontLength];
4031 }
4032
4033 static if (isInfinite!Range)
4034 {
4035 enum bool empty = false; // Propagate infiniteness
4036 }
4037 else
4038 {
emptyResult4039 @property bool empty()
4040 {
4041 return _frontLength == size_t.max && _input.empty;
4042 }
4043 }
4044
popFrontResult4045 void popFront()
4046 {
4047 assert(!empty, "Attempting to popFront an empty splitter.");
4048 ensureFrontLength();
4049 if (_frontLength == _input.length)
4050 {
4051 // done, there's no separator in sight
4052 _input = _input[_frontLength .. _frontLength];
4053 _frontLength = _frontLength.max;
4054 static if (isBidirectionalRange!Range)
4055 _backLength = _backLength.max;
4056 return;
4057 }
4058 if (_frontLength + separatorLength == _input.length)
4059 {
4060 // Special case: popping the first-to-last item; there is
4061 // an empty item right after this.
4062 _input = _input[_input.length .. _input.length];
4063 _frontLength = 0;
4064 static if (isBidirectionalRange!Range)
4065 _backLength = 0;
4066 return;
4067 }
4068 // Normal case, pop one item and the separator, get ready for
4069 // reading the next item
4070 _input = _input[_frontLength + separatorLength .. _input.length];
4071 // mark _frontLength as uninitialized
4072 _frontLength = _frontLength.max;
4073 }
4074
4075 static if (isForwardRange!Range)
4076 {
typeofResult4077 @property typeof(this) save()
4078 {
4079 auto ret = this;
4080 ret._input = _input.save;
4081 return ret;
4082 }
4083 }
4084 }
4085
4086 return Result(r, s);
4087 }
4088
4089 ///
4090 @safe unittest
4091 {
4092 import std.algorithm.comparison : equal;
4093
4094 assert(equal(splitter("hello world", " "), [ "hello", "world" ]));
4095 int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
4096 int[][] w = [ [1, 2], [3, 0, 4, 5, 0] ];
4097 assert(equal(splitter(a, [0, 0]), w));
4098 a = [ 0, 0 ];
4099 assert(equal(splitter(a, [0, 0]), [ (int[]).init, (int[]).init ]));
4100 a = [ 0, 0, 1 ];
4101 assert(equal(splitter(a, [0, 0]), [ [], [1] ]));
4102 }
4103
4104 @safe unittest
4105 {
4106 import std.algorithm.comparison : equal;
4107 import std.typecons : Tuple;
4108
4109 alias C = Tuple!(int, "x", int, "y");
4110 auto a = [C(1,0), C(2,0), C(3,1), C(4,0)];
4111 assert(equal(splitter!"a.x == b"(a, [2, 3]), [ [C(1,0)], [C(4,0)] ]));
4112 }
4113
4114 @safe unittest
4115 {
4116 import std.algorithm.comparison : equal;
4117 import std.array : split;
4118 import std.conv : text;
4119
4120 auto s = ",abc, de, fg,hi,";
4121 auto sp0 = splitter(s, ',');
4122 assert(equal(sp0, ["", "abc", " de", " fg", "hi", ""][]));
4123
4124 auto s1 = ", abc, de, fg, hi, ";
4125 auto sp1 = splitter(s1, ", ");
4126 assert(equal(sp1, ["", "abc", "de", " fg", "hi", ""][]));
4127 static assert(isForwardRange!(typeof(sp1)));
4128
4129 int[] a = [ 1, 2, 0, 3, 0, 4, 5, 0 ];
4130 int[][] w = [ [1, 2], [3], [4, 5], [] ];
4131 uint i;
4132 foreach (e; splitter(a, 0))
4133 {
4134 assert(i < w.length);
4135 assert(e == w[i++]);
4136 }
4137 assert(i == w.length);
4138 // // Now go back
4139 // auto s2 = splitter(a, 0);
4140
4141 // foreach (e; retro(s2))
4142 // {
4143 // assert(i > 0);
4144 // assert(equal(e, w[--i]), text(e));
4145 // }
4146 // assert(i == 0);
4147
4148 wstring names = ",peter,paul,jerry,";
4149 auto words = split(names, ",");
4150 assert(walkLength(words) == 5, text(walkLength(words)));
4151 }
4152
4153 @safe unittest
4154 {
4155 int[][] a = [ [1], [2], [0], [3], [0], [4], [5], [0] ];
4156 int[][][] w = [ [[1], [2]], [[3]], [[4], [5]], [] ];
4157 uint i;
4158 foreach (e; splitter!"a.front == 0"(a, 0))
4159 {
4160 assert(i < w.length);
4161 assert(e == w[i++]);
4162 }
4163 assert(i == w.length);
4164 }
4165
4166 @safe unittest
4167 {
4168 import std.algorithm.comparison : equal;
4169 auto s6 = ",";
4170 auto sp6 = splitter(s6, ',');
foreach(e;sp6)4171 foreach (e; sp6) {}
4172 assert(equal(sp6, ["", ""][]));
4173 }
4174
4175 @safe unittest
4176 {
4177 import std.algorithm.comparison : equal;
4178
4179 // Issue 10773
4180 auto s = splitter("abc", "");
4181 assert(s.equal(["a", "b", "c"]));
4182 }
4183
4184 @safe unittest
4185 {
4186 import std.algorithm.comparison : equal;
4187
4188 // Test by-reference separator
4189 class RefSep {
4190 @safe:
4191 string _impl;
this(string s)4192 this(string s) { _impl = s; }
empty()4193 @property empty() { return _impl.empty; }
front()4194 @property auto front() { return _impl.front; }
popFront()4195 void popFront() { _impl = _impl[1..$]; }
save()4196 @property RefSep save() { return new RefSep(_impl); }
length()4197 @property auto length() { return _impl.length; }
4198 }
4199 auto sep = new RefSep("->");
4200 auto data = "i->am->pointing";
4201 auto words = splitter(data, sep);
4202 assert(words.equal([ "i", "am", "pointing" ]));
4203 }
4204
4205 /**
4206
4207 Similar to the previous overload of $(D splitter), except this one does not use a separator.
4208 Instead, the predicate is an unary function on the input range's element type.
4209 The $(D isTerminator) predicate is passed to $(REF unaryFun, std,functional) and can
4210 either accept a string, or any callable that can be executed via $(D pred(element, s)).
4211
4212 Two adjacent separators are considered to surround an empty element in
4213 the split range. Use $(D filter!(a => !a.empty)) on the result to compress
4214 empty elements.
4215
4216 Params:
4217 isTerminator = The predicate for deciding where to split the range.
4218 input = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to
4219 be split.
4220
4221 Constraints:
4222 The predicate $(D isTerminator) needs to accept an element of $(D input).
4223
4224 Returns:
4225 An input range of the subranges of elements between separators. If $(D input)
4226 is a forward range or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives),
4227 the returned range will be likewise.
4228
4229 See_Also: $(REF _splitter, std,regex) for a version that splits using a regular
4230 expression defined separator.
4231 */
4232 auto splitter(alias isTerminator, Range)(Range input)
4233 if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(input.front))))
4234 {
4235 return SplitterResult!(unaryFun!isTerminator, Range)(input);
4236 }
4237
4238 ///
4239 @safe unittest
4240 {
4241 import std.algorithm.comparison : equal;
4242 import std.range.primitives : front;
4243
4244 assert(equal(splitter!(a => a == ' ')("hello world"), [ "hello", "", "world" ]));
4245 int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
4246 int[][] w = [ [1, 2], [], [3], [4, 5], [] ];
4247 assert(equal(splitter!(a => a == 0)(a), w));
4248 a = [ 0 ];
4249 assert(equal(splitter!(a => a == 0)(a), [ (int[]).init, (int[]).init ]));
4250 a = [ 0, 1 ];
4251 assert(equal(splitter!(a => a == 0)(a), [ [], [1] ]));
4252 w = [ [0], [1], [2] ];
4253 assert(equal(splitter!(a => a.front == 1)(w), [ [[0]], [[2]] ]));
4254 }
4255
SplitterResult(alias isTerminator,Range)4256 private struct SplitterResult(alias isTerminator, Range)
4257 {
4258 import std.algorithm.searching : find;
4259 enum fullSlicing = (hasLength!Range && hasSlicing!Range) || isSomeString!Range;
4260
4261 private Range _input;
4262 private size_t _end = 0;
4263 static if (!fullSlicing)
4264 private Range _next;
4265
4266 private void findTerminator()
4267 {
4268 static if (fullSlicing)
4269 {
4270 auto r = find!isTerminator(_input.save);
4271 _end = _input.length - r.length;
4272 }
4273 else
4274 for ( _end = 0; !_next.empty ; _next.popFront)
4275 {
4276 if (isTerminator(_next.front))
4277 break;
4278 ++_end;
4279 }
4280 }
4281
4282 this(Range input)
4283 {
4284 _input = input;
4285 static if (!fullSlicing)
4286 _next = _input.save;
4287
4288 if (!_input.empty)
4289 findTerminator();
4290 else
4291 _end = size_t.max;
4292 }
4293
4294 static if (isInfinite!Range)
4295 {
4296 enum bool empty = false; // Propagate infiniteness.
4297 }
4298 else
4299 {
4300 @property bool empty()
4301 {
4302 return _end == size_t.max;
4303 }
4304 }
4305
4306 @property auto front()
4307 {
4308 version (assert)
4309 {
4310 import core.exception : RangeError;
4311 if (empty)
4312 throw new RangeError();
4313 }
4314 static if (fullSlicing)
4315 return _input[0 .. _end];
4316 else
4317 {
4318 import std.range : takeExactly;
4319 return _input.takeExactly(_end);
4320 }
4321 }
4322
4323 void popFront()
4324 {
4325 version (assert)
4326 {
4327 import core.exception : RangeError;
4328 if (empty)
4329 throw new RangeError();
4330 }
4331
4332 static if (fullSlicing)
4333 {
4334 _input = _input[_end .. _input.length];
4335 if (_input.empty)
4336 {
4337 _end = size_t.max;
4338 return;
4339 }
4340 _input.popFront();
4341 }
4342 else
4343 {
4344 if (_next.empty)
4345 {
4346 _input = _next;
4347 _end = size_t.max;
4348 return;
4349 }
4350 _next.popFront();
4351 _input = _next.save;
4352 }
4353 findTerminator();
4354 }
4355
4356 @property typeof(this) save()
4357 {
4358 auto ret = this;
4359 ret._input = _input.save;
4360 static if (!fullSlicing)
4361 ret._next = _next.save;
4362 return ret;
4363 }
4364 }
4365
4366 @safe unittest
4367 {
4368 import std.algorithm.comparison : equal;
4369 import std.range : iota;
4370
4371 auto L = iota(1L, 10L);
4372 auto s = splitter(L, [5L, 6L]);
4373 assert(equal(s.front, [1L, 2L, 3L, 4L]));
4374 s.popFront();
4375 assert(equal(s.front, [7L, 8L, 9L]));
4376 s.popFront();
4377 assert(s.empty);
4378 }
4379
4380 @safe unittest
4381 {
4382 import std.algorithm.comparison : equal;
4383 import std.algorithm.internal : algoFormat;
4384 import std.internal.test.dummyrange;
4385
compare(string sentence,string[]witness)4386 void compare(string sentence, string[] witness)
4387 {
4388 auto r = splitter!"a == ' '"(sentence);
4389 assert(equal(r.save, witness), algoFormat("got: %(%s, %) expected: %(%s, %)", r, witness));
4390 }
4391
4392 compare(" Mary has a little lamb. ",
4393 ["", "Mary", "", "has", "a", "little", "lamb.", "", "", ""]);
4394 compare("Mary has a little lamb. ",
4395 ["Mary", "", "has", "a", "little", "lamb.", "", "", ""]);
4396 compare("Mary has a little lamb.",
4397 ["Mary", "", "has", "a", "little", "lamb."]);
4398 compare("", (string[]).init);
4399 compare(" ", ["", ""]);
4400
4401 static assert(isForwardRange!(typeof(splitter!"a == ' '"("ABC"))));
4402
foreach(DummyType;AllDummyRanges)4403 foreach (DummyType; AllDummyRanges)
4404 {
4405 static if (isRandomAccessRange!DummyType)
4406 {
4407 auto rangeSplit = splitter!"a == 5"(DummyType.init);
4408 assert(equal(rangeSplit.front, [1,2,3,4]));
4409 rangeSplit.popFront();
4410 assert(equal(rangeSplit.front, [6,7,8,9,10]));
4411 }
4412 }
4413 }
4414
4415 @safe unittest
4416 {
4417 import std.algorithm.comparison : equal;
4418 import std.algorithm.internal : algoFormat;
4419 import std.range;
4420
4421 struct Entry
4422 {
4423 int low;
4424 int high;
4425 int[][] result;
4426 }
4427 Entry[] entries = [
4428 Entry(0, 0, []),
4429 Entry(0, 1, [[0]]),
4430 Entry(1, 2, [[], []]),
4431 Entry(2, 7, [[2], [4], [6]]),
4432 Entry(1, 8, [[], [2], [4], [6], []]),
4433 ];
foreach(entry;entries)4434 foreach ( entry ; entries )
4435 {
4436 auto a = iota(entry.low, entry.high).filter!"true"();
4437 auto b = splitter!"a%2"(a);
4438 assert(equal!equal(b.save, entry.result), algoFormat("got: %(%s, %) expected: %(%s, %)", b, entry.result));
4439 }
4440 }
4441
4442 @safe unittest
4443 {
4444 import std.algorithm.comparison : equal;
4445 import std.uni : isWhite;
4446
4447 //@@@6791@@@
4448 assert(equal(
4449 splitter("là dove terminava quella valle"),
4450 ["là", "dove", "terminava", "quella", "valle"]
4451 ));
4452 assert(equal(
4453 splitter!(std.uni.isWhite)("là dove terminava quella valle"),
4454 ["là", "dove", "terminava", "quella", "valle"]
4455 ));
4456 assert(equal(splitter!"a=='本'"("日本語"), ["日", "語"]));
4457 }
4458
4459 /++
4460 Lazily splits the string $(D s) into words, using whitespace as the delimiter.
4461
4462 This function is string specific and, contrary to
4463 $(D splitter!(std.uni.isWhite)), runs of whitespace will be merged together
4464 (no empty tokens will be produced).
4465
4466 Params:
4467 s = The string to be split.
4468
4469 Returns:
4470 An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of slices of
4471 the original string split by whitespace.
4472 +/
4473 auto splitter(C)(C[] s)
4474 if (isSomeChar!C)
4475 {
4476 import std.algorithm.searching : find;
4477 static struct Result
4478 {
4479 private:
4480 import core.exception : RangeError;
4481 C[] _s;
4482 size_t _frontLength;
4483
getFirstResult4484 void getFirst() pure @safe
4485 {
4486 import std.uni : isWhite;
4487
4488 auto r = find!(isWhite)(_s);
4489 _frontLength = _s.length - r.length;
4490 }
4491
4492 public:
thisResult4493 this(C[] s) pure @safe
4494 {
4495 import std.string : strip;
4496 _s = s.strip();
4497 getFirst();
4498 }
4499
frontResult4500 @property C[] front() pure @safe
4501 {
4502 version (assert) if (empty) throw new RangeError();
4503 return _s[0 .. _frontLength];
4504 }
4505
popFrontResult4506 void popFront() pure @safe
4507 {
4508 import std.string : stripLeft;
4509 version (assert) if (empty) throw new RangeError();
4510 _s = _s[_frontLength .. $].stripLeft();
4511 getFirst();
4512 }
4513
emptyResult4514 @property bool empty() const @safe pure nothrow
4515 {
4516 return _s.empty;
4517 }
4518
inoutResult4519 @property inout(Result) save() inout @safe pure nothrow
4520 {
4521 return this;
4522 }
4523 }
4524 return Result(s);
4525 }
4526
4527 ///
4528 @safe pure unittest
4529 {
4530 import std.algorithm.comparison : equal;
4531 auto a = " a bcd ef gh ";
4532 assert(equal(splitter(a), ["a", "bcd", "ef", "gh"][]));
4533 }
4534
4535 @safe pure unittest
4536 {
4537 import std.algorithm.comparison : equal;
4538 import std.meta : AliasSeq;
4539 foreach (S; AliasSeq!(string, wstring, dstring))
4540 {
4541 import std.conv : to;
4542 S a = " a bcd ef gh ";
4543 assert(equal(splitter(a), [to!S("a"), to!S("bcd"), to!S("ef"), to!S("gh")]));
4544 a = "";
4545 assert(splitter(a).empty);
4546 }
4547
4548 immutable string s = " a bcd ef gh ";
4549 assert(equal(splitter(s), ["a", "bcd", "ef", "gh"][]));
4550 }
4551
4552 @safe unittest
4553 {
4554 import std.conv : to;
4555 import std.string : strip;
4556
4557 // TDPL example, page 8
4558 uint[string] dictionary;
4559 char[][3] lines;
4560 lines[0] = "line one".dup;
4561 lines[1] = "line \ttwo".dup;
4562 lines[2] = "yah last line\ryah".dup;
foreach(line;lines)4563 foreach (line; lines)
4564 {
4565 foreach (word; splitter(strip(line)))
4566 {
4567 if (word in dictionary) continue; // Nothing to do
4568 auto newID = dictionary.length;
4569 dictionary[to!string(word)] = cast(uint) newID;
4570 }
4571 }
4572 assert(dictionary.length == 5);
4573 assert(dictionary["line"]== 0);
4574 assert(dictionary["one"]== 1);
4575 assert(dictionary["two"]== 2);
4576 assert(dictionary["yah"]== 3);
4577 assert(dictionary["last"]== 4);
4578 }
4579
4580 @safe unittest
4581 {
4582 import std.algorithm.comparison : equal;
4583 import std.algorithm.internal : algoFormat;
4584 import std.array : split;
4585 import std.conv : text;
4586
4587 // Check consistency:
4588 // All flavors of split should produce the same results
foreach(input;[(int[]).init,[0],[0,1,0],[1,1,0,0,1,1],])4589 foreach (input; [(int[]).init,
4590 [0],
4591 [0, 1, 0],
4592 [1, 1, 0, 0, 1, 1],
4593 ])
4594 {
4595 foreach (s; [0, 1])
4596 {
4597 auto result = split(input, s);
4598
4599 assert(equal(result, split(input, [s])), algoFormat(`"[%(%s,%)]"`, split(input, [s])));
4600 //assert(equal(result, split(input, [s].filter!"true"()))); //Not yet implemented
4601 assert(equal(result, split!((a) => a == s)(input)), text(split!((a) => a == s)(input)));
4602
4603 //assert(equal!equal(result, split(input.filter!"true"(), s))); //Not yet implemented
4604 //assert(equal!equal(result, split(input.filter!"true"(), [s]))); //Not yet implemented
4605 //assert(equal!equal(result, split(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented
4606 assert(equal!equal(result, split!((a) => a == s)(input.filter!"true"())));
4607
4608 assert(equal(result, splitter(input, s)));
4609 assert(equal(result, splitter(input, [s])));
4610 //assert(equal(result, splitter(input, [s].filter!"true"()))); //Not yet implemented
4611 assert(equal(result, splitter!((a) => a == s)(input)));
4612
4613 //assert(equal!equal(result, splitter(input.filter!"true"(), s))); //Not yet implemented
4614 //assert(equal!equal(result, splitter(input.filter!"true"(), [s]))); //Not yet implemented
4615 //assert(equal!equal(result, splitter(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented
4616 assert(equal!equal(result, splitter!((a) => a == s)(input.filter!"true"())));
4617 }
4618 }
foreach(input;[string.init," "," hello ","hello hello"," hello what heck this ? "])4619 foreach (input; [string.init,
4620 " ",
4621 " hello ",
4622 "hello hello",
4623 " hello what heck this ? "
4624 ])
4625 {
4626 foreach (s; [' ', 'h'])
4627 {
4628 auto result = split(input, s);
4629
4630 assert(equal(result, split(input, [s])));
4631 //assert(equal(result, split(input, [s].filter!"true"()))); //Not yet implemented
4632 assert(equal(result, split!((a) => a == s)(input)));
4633
4634 //assert(equal!equal(result, split(input.filter!"true"(), s))); //Not yet implemented
4635 //assert(equal!equal(result, split(input.filter!"true"(), [s]))); //Not yet implemented
4636 //assert(equal!equal(result, split(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented
4637 assert(equal!equal(result, split!((a) => a == s)(input.filter!"true"())));
4638
4639 assert(equal(result, splitter(input, s)));
4640 assert(equal(result, splitter(input, [s])));
4641 //assert(equal(result, splitter(input, [s].filter!"true"()))); //Not yet implemented
4642 assert(equal(result, splitter!((a) => a == s)(input)));
4643
4644 //assert(equal!equal(result, splitter(input.filter!"true"(), s))); //Not yet implemented
4645 //assert(equal!equal(result, splitter(input.filter!"true"(), [s]))); //Not yet implemented
4646 //assert(equal!equal(result, splitter(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented
4647 assert(equal!equal(result, splitter!((a) => a == s)(input.filter!"true"())));
4648 }
4649 }
4650 }
4651
4652 // sum
4653 /**
4654 Sums elements of $(D r), which must be a finite
4655 $(REF_ALTTEXT input range, isInputRange, std,range,primitives). Although
4656 conceptually $(D sum(r)) is equivalent to $(LREF fold)!((a, b) => a +
4657 b)(r, 0), $(D sum) uses specialized algorithms to maximize accuracy,
4658 as follows.
4659
4660 $(UL
4661 $(LI If $(D $(REF ElementType, std,range,primitives)!R) is a floating-point
4662 type and $(D R) is a
4663 $(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives) with
4664 length and slicing, then $(D sum) uses the
4665 $(HTTP en.wikipedia.org/wiki/Pairwise_summation, pairwise summation)
4666 algorithm.)
4667 $(LI If $(D ElementType!R) is a floating-point type and $(D R) is a
4668 finite input range (but not a random-access range with slicing), then
4669 $(D sum) uses the $(HTTP en.wikipedia.org/wiki/Kahan_summation,
4670 Kahan summation) algorithm.)
4671 $(LI In all other cases, a simple element by element addition is done.)
4672 )
4673
4674 For floating point inputs, calculations are made in
4675 $(DDLINK spec/type, Types, $(D real))
4676 precision for $(D real) inputs and in $(D double) precision otherwise
4677 (Note this is a special case that deviates from $(D fold)'s behavior,
4678 which would have kept $(D float) precision for a $(D float) range).
4679 For all other types, the calculations are done in the same type obtained
4680 from from adding two elements of the range, which may be a different
4681 type from the elements themselves (for example, in case of
4682 $(DDSUBLINK spec/type,integer-promotions, integral promotion)).
4683
4684 A seed may be passed to $(D sum). Not only will this seed be used as an initial
4685 value, but its type will override all the above, and determine the algorithm
4686 and precision used for summation.
4687
4688 Note that these specialized summing algorithms execute more primitive operations
4689 than vanilla summation. Therefore, if in certain cases maximum speed is required
4690 at expense of precision, one can use $(D fold!((a, b) => a + b)(r, 0)), which
4691 is not specialized for summation.
4692
4693 Params:
4694 seed = the initial value of the summation
4695 r = a finite input range
4696
4697 Returns:
4698 The sum of all the elements in the range r.
4699 */
4700 auto sum(R)(R r)
4701 if (isInputRange!R && !isInfinite!R && is(typeof(r.front + r.front)))
4702 {
4703 alias E = Unqual!(ElementType!R);
4704 static if (isFloatingPoint!E)
4705 alias Seed = typeof(E.init + 0.0); //biggest of double/real
4706 else
4707 alias Seed = typeof(r.front + r.front);
4708 return sum(r, Unqual!Seed(0));
4709 }
4710 /// ditto
4711 auto sum(R, E)(R r, E seed)
4712 if (isInputRange!R && !isInfinite!R && is(typeof(seed = seed + r.front)))
4713 {
4714 static if (isFloatingPoint!E)
4715 {
4716 static if (hasLength!R && hasSlicing!R)
4717 {
4718 if (r.empty) return seed;
4719 return seed + sumPairwise!E(r);
4720 }
4721 else
4722 return sumKahan!E(seed, r);
4723 }
4724 else
4725 {
4726 return reduce!"a + b"(seed, r);
4727 }
4728 }
4729
4730 // Pairwise summation http://en.wikipedia.org/wiki/Pairwise_summation
4731 private auto sumPairwise(F, R)(R data)
4732 if (isInputRange!R && !isInfinite!R)
4733 {
4734 import core.bitop : bsf;
4735 // Works for r with at least length < 2^^(64 + log2(16)), in keeping with the use of size_t
4736 // elsewhere in std.algorithm and std.range on 64 bit platforms. The 16 in log2(16) comes
4737 // from the manual unrolling in sumPairWise16
4738 F[64] store = void;
4739 size_t idx = 0;
4740
collapseStore(T)4741 void collapseStore(T)(T k)
4742 {
4743 auto lastToKeep = idx - cast(uint) bsf(k+1);
4744 while (idx > lastToKeep)
4745 {
4746 store[idx - 1] += store[idx];
4747 --idx;
4748 }
4749 }
4750
4751 static if (hasLength!R)
4752 {
4753 foreach (k; 0 .. data.length / 16)
4754 {
4755 static if (isRandomAccessRange!R && hasSlicing!R)
4756 {
4757 store[idx] = sumPairwise16!F(data);
4758 data = data[16 .. data.length];
4759 }
4760 else store[idx] = sumPairwiseN!(16, false, F)(data);
4761
4762 collapseStore(k);
4763 ++idx;
4764 }
4765
4766 size_t i = 0;
foreach(el;data)4767 foreach (el; data)
4768 {
4769 store[idx] = el;
4770 collapseStore(i);
4771 ++idx;
4772 ++i;
4773 }
4774 }
4775 else
4776 {
4777 size_t k = 0;
4778 while (!data.empty)
4779 {
4780 store[idx] = sumPairwiseN!(16, true, F)(data);
4781 collapseStore(k);
4782 ++idx;
4783 ++k;
4784 }
4785 }
4786
4787 F s = store[idx - 1];
4788 foreach_reverse (j; 0 .. idx - 1)
4789 s += store[j];
4790
4791 return s;
4792 }
4793
4794 private auto sumPairwise16(F, R)(R r)
4795 if (isRandomAccessRange!R)
4796 {
4797 return (((cast(F) r[ 0] + r[ 1]) + (cast(F) r[ 2] + r[ 3]))
4798 + ((cast(F) r[ 4] + r[ 5]) + (cast(F) r[ 6] + r[ 7])))
4799 + (((cast(F) r[ 8] + r[ 9]) + (cast(F) r[10] + r[11]))
4800 + ((cast(F) r[12] + r[13]) + (cast(F) r[14] + r[15])));
4801 }
4802
4803 private auto sumPair(bool needEmptyChecks, F, R)(ref R r)
4804 if (isForwardRange!R && !isRandomAccessRange!R)
4805 {
4806 static if (needEmptyChecks) if (r.empty) return F(0);
4807 F s0 = r.front;
4808 r.popFront();
4809 static if (needEmptyChecks) if (r.empty) return s0;
4810 s0 += r.front;
4811 r.popFront();
4812 return s0;
4813 }
4814
4815 private auto sumPairwiseN(size_t N, bool needEmptyChecks, F, R)(ref R r)
4816 if (isForwardRange!R && !isRandomAccessRange!R)
4817 {
4818 import std.math : isPowerOf2;
4819 static assert(isPowerOf2(N));
4820 static if (N == 2) return sumPair!(needEmptyChecks, F)(r);
4821 else return sumPairwiseN!(N/2, needEmptyChecks, F)(r)
4822 + sumPairwiseN!(N/2, needEmptyChecks, F)(r);
4823 }
4824
4825 // Kahan algo http://en.wikipedia.org/wiki/Kahan_summation_algorithm
sumKahan(Result,R)4826 private auto sumKahan(Result, R)(Result result, R r)
4827 {
4828 static assert(isFloatingPoint!Result && isMutable!Result);
4829 Result c = 0;
4830 for (; !r.empty; r.popFront())
4831 {
4832 immutable y = r.front - c;
4833 immutable t = result + y;
4834 c = (t - result) - y;
4835 result = t;
4836 }
4837 return result;
4838 }
4839
4840 /// Ditto
4841 @safe pure nothrow unittest
4842 {
4843 import std.range;
4844
4845 //simple integral sumation
4846 assert(sum([ 1, 2, 3, 4]) == 10);
4847
4848 //with integral promotion
4849 assert(sum([false, true, true, false, true]) == 3);
4850 assert(sum(ubyte.max.repeat(100)) == 25500);
4851
4852 //The result may overflow
4853 assert(uint.max.repeat(3).sum() == 4294967293U );
4854 //But a seed can be used to change the sumation primitive
4855 assert(uint.max.repeat(3).sum(ulong.init) == 12884901885UL);
4856
4857 //Floating point sumation
4858 assert(sum([1.0, 2.0, 3.0, 4.0]) == 10);
4859
4860 //Floating point operations have double precision minimum
4861 static assert(is(typeof(sum([1F, 2F, 3F, 4F])) == double));
4862 assert(sum([1F, 2, 3, 4]) == 10);
4863
4864 //Force pair-wise floating point sumation on large integers
4865 import std.math : approxEqual;
4866 assert(iota(ulong.max / 2, ulong.max / 2 + 4096).sum(0.0)
4867 .approxEqual((ulong.max / 2) * 4096.0 + 4096^^2 / 2));
4868 }
4869
4870 @safe pure nothrow unittest
4871 {
4872 static assert(is(typeof(sum([cast( byte) 1])) == int));
4873 static assert(is(typeof(sum([cast(ubyte) 1])) == int));
4874 static assert(is(typeof(sum([ 1, 2, 3, 4])) == int));
4875 static assert(is(typeof(sum([ 1U, 2U, 3U, 4U])) == uint));
4876 static assert(is(typeof(sum([ 1L, 2L, 3L, 4L])) == long));
4877 static assert(is(typeof(sum([1UL, 2UL, 3UL, 4UL])) == ulong));
4878
4879 int[] empty;
4880 assert(sum(empty) == 0);
4881 assert(sum([42]) == 42);
4882 assert(sum([42, 43]) == 42 + 43);
4883 assert(sum([42, 43, 44]) == 42 + 43 + 44);
4884 assert(sum([42, 43, 44, 45]) == 42 + 43 + 44 + 45);
4885 }
4886
4887 @safe pure nothrow unittest
4888 {
4889 static assert(is(typeof(sum([1.0, 2.0, 3.0, 4.0])) == double));
4890 static assert(is(typeof(sum([ 1F, 2F, 3F, 4F])) == double));
4891 const(float[]) a = [1F, 2F, 3F, 4F];
4892 assert(sum(a) == 10F);
4893 static assert(is(typeof(sum(a)) == double));
4894
4895 double[] empty;
4896 assert(sum(empty) == 0);
4897 assert(sum([42.]) == 42);
4898 assert(sum([42., 43.]) == 42 + 43);
4899 assert(sum([42., 43., 44.]) == 42 + 43 + 44);
4900 assert(sum([42., 43., 44., 45.5]) == 42 + 43 + 44 + 45.5);
4901 }
4902
4903 @safe pure nothrow unittest
4904 {
4905 import std.container;
4906 static assert(is(typeof(sum(SList!float()[])) == double));
4907 static assert(is(typeof(sum(SList!double()[])) == double));
4908 static assert(is(typeof(sum(SList!real()[])) == real));
4909
4910 assert(sum(SList!double()[]) == 0);
4911 assert(sum(SList!double(1)[]) == 1);
4912 assert(sum(SList!double(1, 2)[]) == 1 + 2);
4913 assert(sum(SList!double(1, 2, 3)[]) == 1 + 2 + 3);
4914 assert(sum(SList!double(1, 2, 3, 4)[]) == 10);
4915 }
4916
4917 @safe pure nothrow unittest // 12434
4918 {
4919 immutable a = [10, 20];
4920 auto s1 = sum(a);
4921 assert(s1 == 30);
4922 auto s2 = a.map!(x => x).sum;
4923 assert(s2 == 30);
4924 }
4925
4926 @system unittest
4927 {
4928 import std.bigint;
4929 import std.range;
4930
4931 immutable BigInt[] a = BigInt("1_000_000_000_000_000_000").repeat(10).array();
4932 immutable ulong[] b = (ulong.max/2).repeat(10).array();
4933 auto sa = a.sum();
4934 auto sb = b.sum(BigInt(0)); //reduce ulongs into bigint
4935 assert(sa == BigInt("10_000_000_000_000_000_000"));
4936 assert(sb == (BigInt(ulong.max/2) * 10));
4937 }
4938
4939 @safe pure nothrow @nogc unittest
4940 {
4941 import std.range;
4942 foreach (n; iota(50))
4943 assert(repeat(1.0, n).sum == n);
4944 }
4945
4946 // uniq
4947 /**
4948 Lazily iterates unique consecutive elements of the given range (functionality
4949 akin to the $(HTTP wikipedia.org/wiki/_Uniq, _uniq) system
4950 utility). Equivalence of elements is assessed by using the predicate
4951 $(D pred), by default $(D "a == b"). The predicate is passed to
4952 $(REF binaryFun, std,functional), and can either accept a string, or any callable
4953 that can be executed via $(D pred(element, element)). If the given range is
4954 bidirectional, $(D uniq) also yields a
4955 $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives).
4956
4957 Params:
4958 pred = Predicate for determining equivalence between range elements.
4959 r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of
4960 elements to filter.
4961
4962 Returns:
4963 An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of
4964 consecutively unique elements in the original range. If $(D r) is also a
4965 forward range or bidirectional range, the returned range will be likewise.
4966 */
4967 auto uniq(alias pred = "a == b", Range)(Range r)
4968 if (isInputRange!Range && is(typeof(binaryFun!pred(r.front, r.front)) == bool))
4969 {
4970 return UniqResult!(binaryFun!pred, Range)(r);
4971 }
4972
4973 ///
4974 @safe unittest
4975 {
4976 import std.algorithm.comparison : equal;
4977 import std.algorithm.mutation : copy;
4978
4979 int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
4980 assert(equal(uniq(arr), [ 1, 2, 3, 4, 5 ][]));
4981
4982 // Filter duplicates in-place using copy
4983 arr.length -= arr.uniq().copy(arr).length;
4984 assert(arr == [ 1, 2, 3, 4, 5 ]);
4985
4986 // Note that uniqueness is only determined consecutively; duplicated
4987 // elements separated by an intervening different element will not be
4988 // eliminated:
4989 assert(equal(uniq([ 1, 1, 2, 1, 1, 3, 1]), [1, 2, 1, 3, 1]));
4990 }
4991
UniqResult(alias pred,Range)4992 private struct UniqResult(alias pred, Range)
4993 {
4994 Range _input;
4995
4996 this(Range input)
4997 {
4998 _input = input;
4999 }
5000
5001 auto opSlice()
5002 {
5003 return this;
5004 }
5005
5006 void popFront()
5007 {
5008 assert(!empty, "Attempting to popFront an empty uniq.");
5009 auto last = _input.front;
5010 do
5011 {
5012 _input.popFront();
5013 }
5014 while (!_input.empty && pred(last, _input.front));
5015 }
5016
5017 @property ElementType!Range front()
5018 {
5019 assert(!empty, "Attempting to fetch the front of an empty uniq.");
5020 return _input.front;
5021 }
5022
5023 static if (isBidirectionalRange!Range)
5024 {
5025 void popBack()
5026 {
5027 assert(!empty, "Attempting to popBack an empty uniq.");
5028 auto last = _input.back;
5029 do
5030 {
5031 _input.popBack();
5032 }
5033 while (!_input.empty && pred(last, _input.back));
5034 }
5035
5036 @property ElementType!Range back()
5037 {
5038 assert(!empty, "Attempting to fetch the back of an empty uniq.");
5039 return _input.back;
5040 }
5041 }
5042
5043 static if (isInfinite!Range)
5044 {
5045 enum bool empty = false; // Propagate infiniteness.
5046 }
5047 else
5048 {
5049 @property bool empty() { return _input.empty; }
5050 }
5051
5052 static if (isForwardRange!Range)
5053 {
5054 @property typeof(this) save() {
5055 return typeof(this)(_input.save);
5056 }
5057 }
5058 }
5059
5060 @safe unittest
5061 {
5062 import std.algorithm.comparison : equal;
5063 import std.internal.test.dummyrange;
5064 import std.range;
5065
5066 int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
5067 auto r = uniq(arr);
5068 static assert(isForwardRange!(typeof(r)));
5069
5070 assert(equal(r, [ 1, 2, 3, 4, 5 ][]));
5071 assert(equal(retro(r), retro([ 1, 2, 3, 4, 5 ][])));
5072
foreach(DummyType;AllDummyRanges)5073 foreach (DummyType; AllDummyRanges)
5074 {
5075 DummyType d;
5076 auto u = uniq(d);
5077 assert(equal(u, [1,2,3,4,5,6,7,8,9,10]));
5078
5079 static assert(d.rt == RangeType.Input || isForwardRange!(typeof(u)));
5080
5081 static if (d.rt >= RangeType.Bidirectional)
5082 {
5083 assert(equal(retro(u), [10,9,8,7,6,5,4,3,2,1]));
5084 }
5085 }
5086 }
5087
5088 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=17264
5089 {
5090 import std.algorithm.comparison : equal;
5091
5092 const(int)[] var = [0, 1, 1, 2];
5093 assert(var.uniq.equal([0, 1, 2]));
5094 }
5095
5096 /**
5097 Lazily computes all _permutations of $(D r) using $(HTTP
5098 en.wikipedia.org/wiki/Heap%27s_algorithm, Heap's algorithm).
5099
5100 Returns:
5101 A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
5102 the elements of which are an $(REF indexed, std,range) view into $(D r).
5103
5104 See_Also:
5105 $(REF nextPermutation, std,algorithm,sorting).
5106 */
5107 Permutations!Range permutations(Range)(Range r)
5108 if (isRandomAccessRange!Range && hasLength!Range)
5109 {
5110 return typeof(return)(r);
5111 }
5112
5113 /// ditto
5114 struct Permutations(Range)
5115 if (isRandomAccessRange!Range && hasLength!Range)
5116 {
5117 private size_t[] _indices, _state;
5118 private Range _r;
5119 private bool _empty;
5120
5121 ///
this(Range r)5122 this(Range r)
5123 {
5124 import std.array : array;
5125 import std.range : iota;
5126
5127 this._r = r;
5128 _state = r.length ? new size_t[r.length-1] : null;
5129 _indices = iota(size_t(r.length)).array;
5130 _empty = r.length == 0;
5131 }
5132
5133 ///
empty()5134 @property bool empty() const pure nothrow @safe @nogc
5135 {
5136 return _empty;
5137 }
5138
5139 ///
front()5140 @property auto front()
5141 {
5142 import std.range : indexed;
5143 return _r.indexed(_indices);
5144 }
5145
5146 ///
popFront()5147 void popFront()
5148 {
5149 void next(int n)
5150 {
5151 import std.algorithm.mutation : swap;
5152
5153 if (n > _indices.length)
5154 {
5155 _empty = true;
5156 return;
5157 }
5158
5159 if (n % 2 == 1)
5160 swap(_indices[0], _indices[n-1]);
5161 else
5162 swap(_indices[_state[n-2]], _indices[n-1]);
5163
5164 if (++_state[n-2] == n)
5165 {
5166 _state[n-2] = 0;
5167 next(n+1);
5168 }
5169 }
5170
5171 next(2);
5172 }
5173 }
5174
5175 ///
5176 @safe unittest
5177 {
5178 import std.algorithm.comparison : equal;
5179 import std.range : iota;
5180 assert(equal!equal(iota(3).permutations,
5181 [[0, 1, 2],
5182 [1, 0, 2],
5183 [2, 0, 1],
5184 [0, 2, 1],
5185 [1, 2, 0],
5186 [2, 1, 0]]));
5187 }
5188