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 `front`.) 11 $(T2 lazyCache, 12 Lazily evaluates and caches another range's `front`, unlike `cache`.) 13 $(T2 cacheBidirectional, 14 As above, but also provides `back` and `popBack`.) 15 $(T2 chunkBy, 16 `chunkBy!((a,b) => a[1] == b[1])([[1, 1], [1, 2], [2, 2], [2, 1]])` 17 returns a range containing 3 subranges: the first with just 18 `[1, 1]`; the second with the elements `[1, 2]` and `[2, 2]`; 19 and the third with just `[2, 1]`.) 20 $(T2 cumulativeFold, 21 `cumulativeFold!((a, b) => a + b)([1, 2, 3, 4])` returns a 22 lazily-evaluated range containing the successive reduced values `1`, 23 `3`, `6`, `10`.) 24 $(T2 each, 25 `each!writeln([1, 2, 3])` eagerly prints the numbers `1`, `2` 26 and `3` on their own lines.) 27 $(T2 filter, 28 `filter!(a => a > 0)([1, -1, 2, 0, -3])` iterates over elements `1` 29 and `2`.) 30 $(T2 filterBidirectional, 31 Similar to `filter`, but also provides `back` and `popBack` at 32 a small increase in cost.) 33 $(T2 fold, 34 `fold!((a, b) => a + b)([1, 2, 3, 4])` returns `10`.) 35 $(T2 group, 36 `group([5, 2, 2, 3, 3])` returns a range containing the tuples 37 `tuple(5, 1)`, `tuple(2, 2)`, and `tuple(3, 2)`.) 38 $(T2 joiner, 39 `joiner(["hello", "world!"], "; ")` returns a range that iterates 40 over the characters `"hello; world!"`. No new string is created - 41 the existing inputs are iterated.) 42 $(T2 map, 43 `map!(a => a * 2)([1, 2, 3])` lazily returns a range with the numbers 44 `2`, `4`, `6`.) 45 $(T2 mean, 46 Colloquially known as the average, `mean([1, 2, 3])` returns `2`.) 47 $(T2 permutations, 48 Lazily computes all permutations using Heap's algorithm.) 49 $(T2 reduce, 50 `reduce!((a, b) => a + b)([1, 2, 3, 4])` returns `10`. 51 This is the old implementation of `fold`.) 52 $(T2 splitWhen, 53 Lazily splits a range by comparing adjacent elements.) 54 $(T2 splitter, 55 Lazily splits a range by a separator, element predicate or whitespace.) 56 $(T2 substitute, 57 `[1, 2].substitute(1, 0.1)` returns `[0.1, 2]`.) 58 $(T2 sum, 59 Same as `fold`, but specialized for accurate summation.) 60 $(T2 uniq, 61 Iterates over the unique elements in a range, which is assumed sorted.) 62 ) 63 64 Copyright: Andrei Alexandrescu 2008-. 65 66 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). 67 68 Authors: $(HTTP erdani.com, Andrei Alexandrescu) 69 70 Source: $(PHOBOSSRC std/algorithm/iteration.d) 71 72 Macros: 73 T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 74 */ 75 module std.algorithm.iteration; 76 77 import std.functional : unaryFun, binaryFun; 78 import std.range.primitives; 79 import std.traits; 80 import std.typecons : Flag, Yes, No; 81 82 /++ 83 `cache` eagerly evaluates $(REF_ALTTEXT front, front, std,range,primitives) of `range` 84 on each construction or call to $(REF_ALTTEXT popFront, popFront, std,range,primitives), 85 to store the result in a _cache. 86 The result is then directly returned when $(REF_ALTTEXT front, front, std,range,primitives) is called, 87 rather than re-evaluated. 88 89 This can be a useful function to place in a chain, after functions 90 that have expensive evaluation, as a lazy alternative to $(REF array, std,array). 91 In particular, it can be placed after a call to $(LREF map), or before a call 92 $(REF filter, std,range) or $(REF tee, std,range) 93 94 `cache` may provide 95 $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) 96 iteration if needed, but since this comes at an increased cost, it must be explicitly requested via the 97 call to `cacheBidirectional`. Furthermore, a bidirectional _cache will 98 evaluate the "center" element twice, when there is only one element left in 99 the range. 100 101 `cache` does not provide random access primitives, 102 as `cache` would be unable to _cache the random accesses. 103 If `Range` provides slicing primitives, 104 then `cache` will provide the same slicing primitives, 105 but `hasSlicing!Cache` will not yield true (as the $(REF hasSlicing, std,range,primitives) 106 trait also checks for random access). 107 108 Params: 109 range = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 110 111 Returns: 112 An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with the cached values of range 113 +/ 114 auto cache(Range)(Range range) 115 if (isInputRange!Range) 116 { 117 return _Cache!(Range, false)(range); 118 } 119 120 /// ditto 121 auto cacheBidirectional(Range)(Range range) 122 if (isBidirectionalRange!Range) 123 { 124 return _Cache!(Range, true)(range); 125 } 126 127 /// 128 @safe unittest 129 { 130 import std.algorithm.comparison : equal; 131 import std.range, std.stdio; 132 import std.typecons : tuple; 133 134 ulong counter = 0; 135 double fun(int x) 136 { 137 ++counter; 138 // http://en.wikipedia.org/wiki/Quartic_function 139 return ( (x + 4.0) * (x + 1.0) * (x - 1.0) * (x - 3.0) ) / 14.0 + 0.5; 140 } 141 // Without cache, with array (greedy) 142 auto result1 = iota(-4, 5).map!(a =>tuple(a, fun(a)))() 143 .filter!(a => a[1] < 0)() 144 .map!(a => a[0])() 145 .array(); 146 147 // the values of x that have a negative y are: 148 assert(equal(result1, [-3, -2, 2])); 149 150 // Check how many times fun was evaluated. 151 // As many times as the number of items in both source and result. 152 assert(counter == iota(-4, 5).length + result1.length); 153 154 counter = 0; 155 // Without array, with cache (lazy) 156 auto result2 = iota(-4, 5).map!(a =>tuple(a, fun(a)))() 157 .cache() 158 .filter!(a => a[1] < 0)() 159 .map!(a => a[0])(); 160 161 // the values of x that have a negative y are: 162 assert(equal(result2, [-3, -2, 2])); 163 164 // Check how many times fun was evaluated. 165 // Only as many times as the number of items in source. 166 assert(counter == iota(-4, 5).length); 167 } 168 169 // https://issues.dlang.org/show_bug.cgi?id=15891 170 @safe pure unittest 171 { 172 assert([1].map!(x=>[x].map!(y=>y)).cache.front.front == 1); 173 } 174 175 /++ 176 Tip: `cache` is eager when evaluating elements. If calling front on the 177 underlying range has a side effect, it will be observable before calling 178 front on the actual cached range. 179 180 Furthermore, care should be taken composing `cache` with $(REF take, std,range). 181 By placing `take` before `cache`, then `cache` will be "aware" 182 of when the range ends, and correctly stop caching elements when needed. 183 If calling front has no side effect though, placing `take` after `cache` 184 may yield a faster range. 185 186 Either way, the resulting ranges will be equivalent, but maybe not at the 187 same cost or side effects. 188 +/ 189 @safe unittest 190 { 191 import std.algorithm.comparison : equal; 192 import std.range; 193 int i = 0; 194 195 auto r = iota(0, 4).tee!((a){i = a;}, No.pipeOnPop); 196 auto r1 = r.take(3).cache(); 197 auto r2 = r.cache().take(3); 198 199 assert(equal(r1, [0, 1, 2])); 200 assert(i == 2); //The last "seen" element was 2. The data in cache has been cleared. 201 202 assert(equal(r2, [0, 1, 2])); 203 assert(i == 3); //cache has accessed 3. It is still stored internally by cache. 204 } 205 206 @safe unittest 207 { 208 import std.algorithm.comparison : equal; 209 import std.range; 210 auto a = [1, 2, 3, 4]; 211 assert(equal(a.map!(a => (a - 1) * a)().cache(), [ 0, 2, 6, 12])); 212 assert(equal(a.map!(a => (a - 1) * a)().cacheBidirectional().retro(), [12, 6, 2, 0])); 213 auto r1 = [1, 2, 3, 4].cache() [1 .. $]; 214 auto r2 = [1, 2, 3, 4].cacheBidirectional()[1 .. $]; 215 assert(equal(r1, [2, 3, 4])); 216 assert(equal(r2, [2, 3, 4])); 217 } 218 219 @safe unittest 220 { 221 import std.algorithm.comparison : equal; 222 223 //immutable test 224 static struct S 225 { 226 int i; 227 this(int i) 228 { 229 //this.i = i; 230 } 231 } 232 immutable(S)[] s = [S(1), S(2), S(3)]; 233 assert(equal(s.cache(), s)); 234 assert(equal(s.cacheBidirectional(), s)); 235 } 236 237 @safe pure nothrow unittest 238 { 239 import std.algorithm.comparison : equal; 240 241 //safety etc 242 auto a = [1, 2, 3, 4]; 243 assert(equal(a.cache(), a)); 244 assert(equal(a.cacheBidirectional(), a)); 245 } 246 247 @safe unittest 248 { 249 char[][] stringbufs = ["hello".dup, "world".dup]; 250 auto strings = stringbufs.map!((a)=>a.idup)().cache(); 251 assert(strings.front is strings.front); 252 } 253 254 @safe unittest 255 { 256 import std.range : cycle; 257 import std.algorithm.comparison : equal; 258 259 auto c = [1, 2, 3].cycle().cache(); 260 c = c[1 .. $]; 261 auto d = c[0 .. 1]; 262 assert(d.equal([2])); 263 } 264 265 @safe unittest 266 { 267 static struct Range 268 { 269 bool initialized = false; 270 bool front() @property {return initialized = true;} 271 void popFront() {initialized = false;} 272 enum empty = false; 273 } 274 auto r = Range().cache(); 275 assert(r.source.initialized == true); 276 } 277 278 private struct _Cache(R, bool bidir) 279 { 280 import core.exception : RangeError; 281 282 private 283 { 284 import std.algorithm.internal : algoFormat; 285 import std.meta : AliasSeq; 286 287 alias E = ElementType!R; 288 alias UE = Unqual!E; 289 290 R source; 291 292 static if (bidir) alias CacheTypes = AliasSeq!(UE, UE); 293 else alias CacheTypes = AliasSeq!UE; 294 CacheTypes caches; 295 296 static assert(isAssignable!(UE, E) && is(UE : E), 297 algoFormat( 298 "Cannot instantiate range with %s because %s elements are not assignable to %s.", 299 R.stringof, 300 E.stringof, 301 UE.stringof 302 ) 303 ); 304 } 305 306 this(R range) 307 { 308 source = range; 309 if (!range.empty) 310 { 311 caches[0] = source.front; 312 static if (bidir) 313 caches[1] = source.back; 314 } 315 else 316 { 317 // needed, because the compiler cannot deduce, that 'caches' is initialized 318 // see https://issues.dlang.org/show_bug.cgi?id=15891 319 caches[0] = UE.init; 320 static if (bidir) 321 caches[1] = UE.init; 322 } 323 } 324 325 static if (isInfinite!R) 326 enum empty = false; 327 else 328 bool empty() @property 329 { 330 return source.empty; 331 } 332 333 mixin ImplementLength!source; 334 335 E front() @property 336 { 337 version (assert) if (empty) throw new RangeError(); 338 return caches[0]; 339 } 340 static if (bidir) E back() @property 341 { 342 version (assert) if (empty) throw new RangeError(); 343 return caches[1]; 344 } 345 346 void popFront() 347 { 348 version (assert) if (empty) throw new RangeError(); 349 source.popFront(); 350 if (!source.empty) 351 caches[0] = source.front; 352 else 353 { 354 // see https://issues.dlang.org/show_bug.cgi?id=15891 355 caches[0] = UE.init; 356 static if (bidir) 357 caches[1] = UE.init; 358 } 359 } 360 static if (bidir) void popBack() 361 { 362 version (assert) if (empty) throw new RangeError(); 363 source.popBack(); 364 if (!source.empty) 365 caches[1] = source.back; 366 else 367 { 368 // see https://issues.dlang.org/show_bug.cgi?id=15891 369 caches[0] = UE.init; 370 caches[1] = UE.init; 371 } 372 } 373 374 static if (isForwardRange!R) 375 { 376 private this(R source, ref CacheTypes caches) 377 { 378 this.source = source; 379 this.caches = caches; 380 } 381 typeof(this) save() @property 382 { 383 return typeof(this)(source.save, caches); 384 } 385 } 386 387 static if (hasSlicing!R) 388 { 389 enum hasEndSlicing = is(typeof(source[size_t.max .. $])); 390 391 static if (hasEndSlicing) 392 { 393 private static struct DollarToken{} 394 enum opDollar = DollarToken.init; 395 396 auto opSlice(size_t low, DollarToken) 397 { 398 return typeof(this)(source[low .. $]); 399 } 400 } 401 402 static if (!isInfinite!R) 403 { 404 typeof(this) opSlice(size_t low, size_t high) 405 { 406 return typeof(this)(source[low .. high]); 407 } 408 } 409 else static if (hasEndSlicing) 410 { 411 auto opSlice(size_t low, size_t high) 412 in 413 { 414 assert(low <= high, "Bounds error when slicing cache."); 415 } 416 do 417 { 418 import std.range : takeExactly; 419 return this[low .. $].takeExactly(high - low); 420 } 421 } 422 } 423 } 424 425 /** 426 * Similar to `cache`, but lazily evaluates the elements. Unlike `cache`, 427 * this function does not eagerly evaluate the front of the range until 428 * it's explicitly requested. 429 * 430 * This can be useful when evaluation of range elements has side effects that 431 * should be delayed until actually needed. 432 * 433 * See_Also: cache 434 * 435 * Params: 436 * range = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 437 * 438 * Returns: 439 * An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with the lazily cached values of range 440 */ 441 442 auto lazyCache(Range)(Range range) 443 if (isInputRange!Range) 444 { 445 return LazyCache!(Range, isBidirectionalRange!Range)(range); 446 } 447 448 /// 449 @safe unittest 450 { 451 import std.algorithm.comparison : equal; 452 import std.range, std.stdio; 453 import std.typecons : tuple; 454 455 ulong counter = 0; 456 double fun(int x) 457 { 458 ++counter; 459 // http://en.wikipedia.org/wiki/Quartic_function 460 return ( (x + 4.0) * (x + 1.0) * (x - 1.0) * (x - 3.0) ) / 14.0 + 0.5; 461 } 462 // With lazyCache, front won't be evaluated until requested 463 counter = 0; 464 auto result = iota(-4, 5).map!(a => tuple(a, fun(a)))() 465 .lazyCache(); 466 // At this point, no elements have been evaluated yet 467 assert(counter == 0); 468 // Now access the first element 469 auto firstElement = result.front; 470 // Only now the first element is evaluated 471 assert(counter == 1); 472 // Process the result lazily 473 auto filtered = result.filter!(a => a[1] < 0)() 474 .map!(a => a[0])(); 475 // Values are calculated as we iterate 476 assert(equal(filtered, [-3, -2, 2])); 477 // Only elements we actually accessed were evaluated 478 assert(counter == iota(-4, 5).length); 479 } 480 481 private struct LazyCache(R, bool bidir) 482 { 483 import core.exception : RangeError; 484 485 private 486 { 487 import std.algorithm.internal : algoFormat; 488 import std.meta : AliasSeq; 489 490 alias E = ElementType!R; 491 alias UE = Unqual!E; 492 493 R source; 494 495 static if (bidir) alias CacheTypes = AliasSeq!(UE, UE); 496 else alias CacheTypes = AliasSeq!UE; 497 CacheTypes caches; 498 // Flags to track if front/back have been cached 499 bool frontCached = false; 500 static if (bidir) bool backCached = false; 501 502 static assert(isAssignable!(UE, E) && is(UE : E), 503 algoFormat( 504 "Cannot instantiate range with %s because %s elements are not assignable to %s.", 505 R.stringof, 506 E.stringof, 507 UE.stringof 508 ) 509 ); 510 } 511 512 this(R range) 513 { 514 source = range; 515 // Don't eagerly evaluate anything in the constructor 516 } 517 518 static if (isInfinite!R) 519 enum empty = false; 520 else 521 bool empty() @property 522 { 523 return source.empty; 524 } 525 526 mixin ImplementLength!source; 527 528 E front() @property 529 { 530 version (assert) if (empty) throw new RangeError(); 531 // Only evaluate front if it hasn't been cached yet 532 if (!frontCached && !source.empty) 533 { 534 caches[0] = source.front; 535 frontCached = true; 536 } 537 return caches[0]; 538 } 539 static if (bidir) E back() @property 540 { 541 version (assert) if (empty) throw new RangeError(); 542 // Only evaluate back if it hasn't been cached yet 543 if (!backCached && !source.empty) 544 { 545 caches[1] = source.back; 546 backCached = true; 547 } 548 return caches[1]; 549 } 550 551 void popFront() 552 { 553 version (assert) if (empty) throw new RangeError(); 554 source.popFront(); 555 // Reset the cache state for front 556 frontCached = false; 557 } 558 static if (bidir) void popBack() 559 { 560 version (assert) if (empty) throw new RangeError(); 561 source.popBack(); 562 // Reset the cache state for back 563 backCached = false; 564 } 565 566 static if (isForwardRange!R) 567 { 568 private this(R source, ref CacheTypes caches, bool frontCached, bool backCached = false) 569 { 570 this.source = source; 571 this.caches = caches; 572 this.frontCached = frontCached; 573 static if (bidir) this.backCached = backCached; 574 } 575 typeof(this) save() @property 576 { 577 static if (bidir) 578 return typeof(this)(source.save, caches, frontCached, backCached); 579 else 580 return typeof(this)(source.save, caches, frontCached); 581 } 582 } 583 584 static if (hasSlicing!R) 585 { 586 enum hasEndSlicing = is(typeof(source[size_t.max .. $])); 587 588 static if (hasEndSlicing) 589 { 590 private static struct DollarToken{} 591 enum opDollar = DollarToken.init; 592 593 auto opSlice(size_t low, DollarToken) 594 { 595 return typeof(this)(source[low .. $]); 596 } 597 } 598 599 static if (!isInfinite!R) 600 { 601 typeof(this) opSlice(size_t low, size_t high) 602 { 603 return typeof(this)(source[low .. high]); 604 } 605 } 606 else static if (hasEndSlicing) 607 { 608 auto opSlice(size_t low, size_t high) 609 in 610 { 611 assert(low <= high, "Bounds error when slicing lazyCache."); 612 } 613 do 614 { 615 import std.range : takeExactly; 616 return this[low .. $].takeExactly(high - low); 617 } 618 } 619 } 620 } 621 622 /** 623 Implements the homonym function (also known as `transform`) present 624 in many languages of functional flavor. The call `map!(fun)(range)` 625 returns a range of which elements are obtained by applying `fun(a)` 626 left to right for all elements `a` in `range`. The original ranges are 627 not changed. Evaluation is done lazily. 628 629 Params: 630 fun = one or more transformation functions 631 632 See_Also: 633 $(HTTP en.wikipedia.org/wiki/Map_(higher-order_function), Map (higher-order function)) 634 */ 635 template map(fun...) 636 if (fun.length >= 1) 637 { 638 /** 639 Params: 640 r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 641 Returns: 642 A range with each fun applied to all the elements. If there is more than one 643 fun, the element type will be `Tuple` containing one element for each fun. 644 */ 645 auto map(Range)(Range r) 646 if (isInputRange!(Unqual!Range)) 647 { 648 import std.meta : staticMap; 649 import std.functional : adjoin; 650 651 alias RE = ElementType!(Range); 652 653 alias _funs = staticMap!(unaryFun, fun); 654 alias _fun = adjoin!_funs; 655 656 // Once https://issues.dlang.org/show_bug.cgi?id=5710 is fixed 657 // accross all compilers (as of 2020-04, it wasn't fixed in LDC and GDC), 658 // this validation loop can be moved into a template. 659 foreach (f; _funs) 660 { 661 static assert(!is(typeof(f(RE.init)) == void), 662 "Mapping function(s) must not return void: " ~ _funs.stringof); 663 } 664 665 return MapResult!(_fun, Range)(r); 666 } 667 } 668 669 /// 670 @safe @nogc unittest 671 { 672 import std.algorithm.comparison : equal; 673 import std.range : chain, only; 674 auto squares = 675 chain(only(1, 2, 3, 4), only(5, 6)).map!(a => a * a); 676 assert(equal(squares, only(1, 4, 9, 16, 25, 36))); 677 } 678 679 /** 680 Multiple functions can be passed to `map`. In that case, the 681 element type of `map` is a tuple containing one element for each 682 function. 683 */ 684 @safe unittest 685 { 686 auto sums = [2, 4, 6, 8]; 687 auto products = [1, 4, 9, 16]; 688 689 size_t i = 0; 690 foreach (result; [ 1, 2, 3, 4 ].map!("a + a", "a * a")) 691 { 692 assert(result[0] == sums[i]); 693 assert(result[1] == products[i]); 694 ++i; 695 } 696 } 697 698 /** 699 You may alias `map` with some function(s) to a symbol and use 700 it separately: 701 */ 702 @safe unittest 703 { 704 import std.algorithm.comparison : equal; 705 import std.conv : to; 706 707 alias stringize = map!(to!string); 708 assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ])); 709 } 710 711 // Verify workaround for https://issues.dlang.org/show_bug.cgi?id=15777 712 @safe unittest 713 { 714 import std.algorithm.mutation, std.string; 715 auto foo(string[] args) 716 { 717 return args.map!strip; 718 } 719 } 720 721 private struct MapResult(alias fun, Range) 722 { 723 alias R = Unqual!Range; 724 R _input; 725 726 static if (isBidirectionalRange!R) 727 { 728 @property auto ref back()() 729 { 730 assert(!empty, "Attempting to fetch the back of an empty map."); 731 return fun(_input.back); 732 } 733 734 void popBack()() 735 { 736 assert(!empty, "Attempting to popBack an empty map."); 737 _input.popBack(); 738 } 739 } 740 741 this(R input) 742 { 743 _input = input; 744 } 745 746 static if (isInfinite!R) 747 { 748 // Propagate infinite-ness. 749 enum bool empty = false; 750 } 751 else 752 { 753 @property bool empty() 754 { 755 return _input.empty; 756 } 757 } 758 759 void popFront() 760 { 761 assert(!empty, "Attempting to popFront an empty map."); 762 _input.popFront(); 763 } 764 765 @property auto ref front() 766 { 767 assert(!empty, "Attempting to fetch the front of an empty map."); 768 return fun(_input.front); 769 } 770 771 static if (isRandomAccessRange!R) 772 { 773 static if (is(typeof(Range.init[ulong.max]))) 774 private alias opIndex_t = ulong; 775 else 776 private alias opIndex_t = uint; 777 778 auto ref opIndex(opIndex_t index) 779 { 780 return fun(_input[index]); 781 } 782 } 783 784 mixin ImplementLength!_input; 785 786 static if (hasSlicing!R) 787 { 788 static if (is(typeof(_input[ulong.max .. ulong.max]))) 789 private alias opSlice_t = ulong; 790 else 791 private alias opSlice_t = uint; 792 793 static if (hasLength!R) 794 { 795 auto opSlice(opSlice_t low, opSlice_t high) 796 { 797 return typeof(this)(_input[low .. high]); 798 } 799 } 800 else static if (is(typeof(_input[opSlice_t.max .. $]))) 801 { 802 struct DollarToken{} 803 enum opDollar = DollarToken.init; 804 auto opSlice(opSlice_t low, DollarToken) 805 { 806 return typeof(this)(_input[low .. $]); 807 } 808 809 auto opSlice(opSlice_t low, opSlice_t high) 810 { 811 import std.range : takeExactly; 812 return this[low .. $].takeExactly(high - low); 813 } 814 } 815 } 816 817 static if (isForwardRange!R) 818 { 819 @property auto save() 820 { 821 return typeof(this)(_input.save); 822 } 823 } 824 } 825 826 @safe unittest 827 { 828 import std.algorithm.comparison : equal; 829 import std.conv : to; 830 import std.functional : adjoin; 831 832 alias stringize = map!(to!string); 833 assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ])); 834 835 uint counter; 836 alias count = map!((a) { return counter++; }); 837 assert(equal(count([ 10, 2, 30, 4 ]), [ 0, 1, 2, 3 ])); 838 839 counter = 0; 840 adjoin!((a) { return counter++; }, (a) { return counter++; })(1); 841 alias countAndSquare = map!((a) { return counter++; }, (a) { return counter++; }); 842 //assert(equal(countAndSquare([ 10, 2 ]), [ tuple(0u, 100), tuple(1u, 4) ])); 843 } 844 845 @safe unittest 846 { 847 import std.algorithm.comparison : equal; 848 import std.ascii : toUpper; 849 import std.internal.test.dummyrange; 850 import std.range; 851 import std.typecons : tuple; 852 import std.random : uniform, Random = Xorshift; 853 854 int[] arr1 = [ 1, 2, 3, 4 ]; 855 const int[] arr1Const = arr1; 856 int[] arr2 = [ 5, 6 ]; 857 auto squares = map!("a * a")(arr1Const); 858 assert(squares[$ - 1] == 16); 859 assert(equal(squares, [ 1, 4, 9, 16 ][])); 860 assert(equal(map!("a * a")(chain(arr1, arr2)), [ 1, 4, 9, 16, 25, 36 ][])); 861 862 // Test the caching stuff. 863 assert(squares.back == 16); 864 auto squares2 = squares.save; 865 assert(squares2.back == 16); 866 867 assert(squares2.front == 1); 868 squares2.popFront(); 869 assert(squares2.front == 4); 870 squares2.popBack(); 871 assert(squares2.front == 4); 872 assert(squares2.back == 9); 873 874 assert(equal(map!("a * a")(chain(arr1, arr2)), [ 1, 4, 9, 16, 25, 36 ][])); 875 876 uint i; 877 foreach (e; map!("a", "a * a")(arr1)) 878 { 879 assert(e[0] == ++i); 880 assert(e[1] == i * i); 881 } 882 883 // Test length. 884 assert(squares.length == 4); 885 assert(map!"a * a"(chain(arr1, arr2)).length == 6); 886 887 // Test indexing. 888 assert(squares[0] == 1); 889 assert(squares[1] == 4); 890 assert(squares[2] == 9); 891 assert(squares[3] == 16); 892 893 // Test slicing. 894 auto squareSlice = squares[1 .. squares.length - 1]; 895 assert(equal(squareSlice, [4, 9][])); 896 assert(squareSlice.back == 9); 897 assert(squareSlice[1] == 9); 898 899 // Test on a forward range to make sure it compiles when all the fancy 900 // stuff is disabled. 901 auto fibsSquares = map!"a * a"(recurrence!("a[n-1] + a[n-2]")(1, 1)); 902 assert(fibsSquares.front == 1); 903 fibsSquares.popFront(); 904 fibsSquares.popFront(); 905 assert(fibsSquares.front == 4); 906 fibsSquares.popFront(); 907 assert(fibsSquares.front == 9); 908 909 auto repeatMap = map!"a"(repeat(1)); 910 auto gen = Random(123_456_789); 911 auto index = uniform(0, 1024, gen); 912 static assert(isInfinite!(typeof(repeatMap))); 913 assert(repeatMap[index] == 1); 914 915 auto intRange = map!"a"([1,2,3]); 916 static assert(isRandomAccessRange!(typeof(intRange))); 917 assert(equal(intRange, [1, 2, 3])); 918 919 foreach (DummyType; AllDummyRanges) 920 { 921 DummyType d; 922 auto m = map!"a * a"(d); 923 924 static assert(propagatesRangeType!(typeof(m), DummyType)); 925 assert(equal(m, [1,4,9,16,25,36,49,64,81,100])); 926 } 927 928 //Test string access 929 string s1 = "hello world!"; 930 dstring s2 = "日本語"; 931 dstring s3 = "hello world!"d; 932 auto ms1 = map!(toUpper)(s1); 933 auto ms2 = map!(toUpper)(s2); 934 auto ms3 = map!(toUpper)(s3); 935 static assert(!is(ms1[0])); //narrow strings can't be indexed 936 assert(ms2[0] == '日'); 937 assert(ms3[0] == 'H'); 938 static assert(!is(ms1[0 .. 1])); //narrow strings can't be sliced 939 assert(equal(ms2[0 .. 2], "日本"w)); 940 assert(equal(ms3[0 .. 2], "HE")); 941 942 // https://issues.dlang.org/show_bug.cgi?id=5753 943 static void voidFun(int) {} 944 static int nonvoidFun(int) { return 0; } 945 static assert(!__traits(compiles, map!voidFun([1]))); 946 static assert(!__traits(compiles, map!(voidFun, voidFun)([1]))); 947 static assert(!__traits(compiles, map!(nonvoidFun, voidFun)([1]))); 948 static assert(!__traits(compiles, map!(voidFun, nonvoidFun)([1]))); 949 static assert(!__traits(compiles, map!(a => voidFun(a))([1]))); 950 951 // https://issues.dlang.org/show_bug.cgi?id=15480 952 auto dd = map!(z => z * z, c => c * c * c)([ 1, 2, 3, 4 ]); 953 assert(dd[0] == tuple(1, 1)); 954 assert(dd[1] == tuple(4, 8)); 955 assert(dd[2] == tuple(9, 27)); 956 assert(dd[3] == tuple(16, 64)); 957 assert(dd.length == 4); 958 } 959 960 // Verify fix for: https://issues.dlang.org/show_bug.cgi?id=16034 961 @safe unittest 962 { 963 struct One 964 { 965 int entry = 1; 966 @disable this(this); 967 } 968 969 One[] ones = [One(), One()]; 970 971 import std.algorithm.comparison : equal; 972 973 assert(ones.map!`a.entry + 1`.equal([2, 2])); 974 } 975 976 977 @safe unittest 978 { 979 import std.algorithm.comparison : equal; 980 import std.range; 981 auto LL = iota(1L, 4L); 982 auto m = map!"a*a"(LL); 983 assert(equal(m, [1L, 4L, 9L])); 984 } 985 986 @safe unittest 987 { 988 import std.range : iota; 989 990 // https://issues.dlang.org/show_bug.cgi?id=10130 - map of iota with const step. 991 const step = 2; 992 assert(map!(i => i)(iota(0, 10, step)).walkLength == 5); 993 994 // Need these to all by const to repro the float case, due to the 995 // CommonType template used in the float specialization of iota. 996 const floatBegin = 0.0; 997 const floatEnd = 1.0; 998 const floatStep = 0.02; 999 assert(map!(i => i)(iota(floatBegin, floatEnd, floatStep)).walkLength == 50); 1000 } 1001 1002 @safe unittest 1003 { 1004 import std.algorithm.comparison : equal; 1005 import std.range; 1006 //slicing infinites 1007 auto rr = iota(0, 5).cycle().map!"a * a"(); 1008 alias RR = typeof(rr); 1009 static assert(hasSlicing!RR); 1010 rr = rr[6 .. $]; //Advances 1 cycle and 1 unit 1011 assert(equal(rr[0 .. 5], [1, 4, 9, 16, 0])); 1012 } 1013 1014 @safe unittest 1015 { 1016 import std.range; 1017 struct S {int* p;} 1018 auto m = immutable(S).init.repeat().map!"a".save; 1019 assert(m.front == immutable(S)(null)); 1020 } 1021 1022 // Issue 20928 1023 @safe unittest 1024 { 1025 struct Always3 1026 { 1027 enum empty = false; 1028 auto save() { return this; } 1029 long front() { return 3; } 1030 void popFront() {} 1031 long opIndex(ulong i) { return 3; } 1032 long opIndex(ulong i) immutable { return 3; } 1033 } 1034 1035 import std.algorithm.iteration : map; 1036 Always3.init.map!(e => e)[ulong.max]; 1037 } 1038 1039 // each 1040 /** 1041 Eagerly iterates over `r` and calls `fun` with _each element. 1042 1043 If no function to call is specified, `each` defaults to doing nothing but 1044 consuming the entire range. `r.front` will be evaluated, but that can be avoided 1045 by specifying a lambda with a `lazy` parameter. 1046 1047 `each` also supports `opApply`-based types, so it works with e.g. $(REF 1048 parallel, std,parallelism). 1049 1050 Normally the entire range is iterated. If partial iteration (early stopping) is 1051 desired, `fun` needs to return a value of type $(REF Flag, 1052 std,typecons)`!"each"` (`Yes.each` to continue iteration, or `No.each` to stop 1053 iteration). 1054 1055 Params: 1056 fun = function to apply to _each element of the range 1057 r = range or iterable over which `each` iterates 1058 1059 Returns: `Yes.each` if the entire range was iterated, `No.each` in case of early 1060 stopping. 1061 1062 See_Also: $(REF tee, std,range) 1063 */ 1064 template each(alias fun = "a") 1065 { 1066 import std.meta : AliasSeq; 1067 import std.traits : Parameters, isInstanceOf; 1068 import std.typecons : Flag, Yes, No, Tuple; 1069 1070 private: 1071 alias BinaryArgs = AliasSeq!(fun, "i", "a"); 1072 1073 enum isRangeUnaryIterable(R) = 1074 is(typeof(unaryFun!fun(R.init.front))); 1075 1076 enum isRangeBinaryIterable(R) = 1077 is(typeof(binaryFun!BinaryArgs(0, R.init.front))); 1078 1079 enum isRangeIterable(R) = 1080 isInputRange!R && 1081 (isRangeUnaryIterable!R || isRangeBinaryIterable!R); 1082 1083 enum isForeachUnaryIterable(R) = 1084 is(typeof((R r) { 1085 foreach (ref a; r) 1086 cast(void) unaryFun!fun(a); 1087 })); 1088 1089 enum isForeachUnaryWithIndexIterable(R) = 1090 is(typeof((R r) { 1091 foreach (i, ref a; r) 1092 cast(void) binaryFun!BinaryArgs(i, a); 1093 })); 1094 1095 enum isForeachBinaryIterable(R) = 1096 is(typeof((R r) { 1097 foreach (ref a, ref b; r) 1098 cast(void) binaryFun!fun(a, b); 1099 })); 1100 1101 enum isForeachIterable(R) = 1102 (!isForwardRange!R || isDynamicArray!R) && 1103 (isForeachUnaryIterable!R || isForeachBinaryIterable!R || 1104 isForeachUnaryWithIndexIterable!R); 1105 1106 public: 1107 /** 1108 Params: 1109 r = range or iterable over which each iterates 1110 */ 1111 Flag!"each" each(Range)(Range r) 1112 if (!isForeachIterable!Range && ( 1113 isRangeIterable!Range || 1114 // Tuples get automatically expanded if function takes right number of args 1115 (isInputRange!Range && isInstanceOf!(Tuple, typeof(r.front))))) 1116 { 1117 static if (isRangeIterable!Range) 1118 { 1119 debug(each) pragma(msg, "Using while for ", Range.stringof); 1120 static if (isRangeUnaryIterable!Range) 1121 { 1122 while (!r.empty) 1123 { 1124 static if (!is(typeof(unaryFun!fun(r.front)) == Flag!"each")) 1125 { 1126 cast(void) unaryFun!fun(r.front); 1127 } 1128 else 1129 { 1130 if (unaryFun!fun(r.front) == No.each) return No.each; 1131 } 1132 1133 r.popFront(); 1134 } 1135 } 1136 else // if (isRangeBinaryIterable!Range) 1137 { 1138 size_t i = 0; 1139 while (!r.empty) 1140 { 1141 static if (!is(typeof(binaryFun!BinaryArgs(i, r.front)) == Flag!"each")) 1142 { 1143 cast(void) binaryFun!BinaryArgs(i, r.front); 1144 } 1145 else 1146 { 1147 if (binaryFun!BinaryArgs(i, r.front) == No.each) return No.each; 1148 } 1149 r.popFront(); 1150 i++; 1151 } 1152 } 1153 } 1154 else 1155 { 1156 /* Range over Tuples, fun must have correct number of args 1157 * If number of args of fun is <= 2, calling fun(tuple) or fun(idx, tuple) 1158 * will have precendence over fun(tuple.expand). 1159 * Provide types to delegate params to control this. 1160 */ 1161 for (auto range = r; !range.empty; range.popFront()) 1162 { 1163 static if (!is(typeof(fun(r.front.expand)) == Flag!"each")) 1164 { 1165 cast(void) fun(range.front.expand); 1166 } 1167 else 1168 { 1169 if (fun(range.front.expand)) return No.each; 1170 } 1171 } 1172 } 1173 return Yes.each; 1174 } 1175 1176 /// ditto 1177 Flag!"each" each(Iterable)(auto ref Iterable r) 1178 if (isForeachIterable!Iterable || 1179 __traits(compiles, Parameters!(Parameters!(r.opApply)))) 1180 { 1181 static if (isForeachIterable!Iterable) 1182 { 1183 static if (isForeachUnaryIterable!Iterable) 1184 { 1185 debug(each) pragma(msg, "Using foreach UNARY for ", Iterable.stringof); 1186 { 1187 foreach (ref e; r) 1188 { 1189 static if (!is(typeof(unaryFun!fun(e)) == Flag!"each")) 1190 { 1191 cast(void) unaryFun!fun(e); 1192 } 1193 else 1194 { 1195 if (unaryFun!fun(e) == No.each) return No.each; 1196 } 1197 } 1198 } 1199 } 1200 else static if (isForeachBinaryIterable!Iterable) 1201 { 1202 debug(each) pragma(msg, "Using foreach BINARY for ", Iterable.stringof); 1203 foreach (ref a, ref b; r) 1204 { 1205 static if (!is(typeof(binaryFun!fun(a, b)) == Flag!"each")) 1206 { 1207 cast(void) binaryFun!fun(a, b); 1208 } 1209 else 1210 { 1211 if (binaryFun!fun(a, b) == No.each) return No.each; 1212 } 1213 } 1214 } 1215 else static if (isForeachUnaryWithIndexIterable!Iterable) 1216 { 1217 debug(each) pragma(msg, "Using foreach INDEX for ", Iterable.stringof); 1218 foreach (i, ref e; r) 1219 { 1220 static if (!is(typeof(binaryFun!BinaryArgs(i, e)) == Flag!"each")) 1221 { 1222 cast(void) binaryFun!BinaryArgs(i, e); 1223 } 1224 else 1225 { 1226 if (binaryFun!BinaryArgs(i, e) == No.each) return No.each; 1227 } 1228 } 1229 } 1230 else 1231 { 1232 static assert(0, "Invalid foreach iteratable type " ~ Iterable.stringof ~ " met."); 1233 } 1234 return Yes.each; 1235 } 1236 else 1237 { 1238 // opApply with >2 parameters. count the delegate args. 1239 // only works if it is not templated (otherwise we cannot count the args) 1240 auto result = Yes.each; 1241 auto dg(Parameters!(Parameters!(r.opApply)) params) 1242 { 1243 static if (!is(typeof(binaryFun!BinaryArgs(i, e)) == Flag!"each")) 1244 { 1245 fun(params); 1246 return 0; // tells opApply to continue iteration 1247 } 1248 else 1249 { 1250 result = fun(params); 1251 return result == Yes.each ? 0 : -1; 1252 } 1253 } 1254 r.opApply(&dg); 1255 return result; 1256 } 1257 } 1258 } 1259 1260 /// 1261 @safe unittest 1262 { 1263 import std.range : iota; 1264 import std.typecons : No; 1265 1266 int[] arr; 1267 iota(5).each!(n => arr ~= n); 1268 assert(arr == [0, 1, 2, 3, 4]); 1269 1270 // stop iterating early 1271 iota(5).each!((n) { arr ~= n; return No.each; }); 1272 assert(arr == [0, 1, 2, 3, 4, 0]); 1273 1274 // If the range supports it, the value can be mutated in place 1275 arr.each!((ref n) => n++); 1276 assert(arr == [1, 2, 3, 4, 5, 1]); 1277 1278 arr.each!"a++"; 1279 assert(arr == [2, 3, 4, 5, 6, 2]); 1280 1281 auto m = arr.map!(n => n); 1282 // by-ref lambdas are not allowed for non-ref ranges 1283 static assert(!__traits(compiles, m.each!((ref n) => n++))); 1284 1285 // The default predicate consumes the range 1286 (&m).each(); 1287 assert(m.empty); 1288 } 1289 1290 /// `each` can pass an index variable for iterable objects which support this 1291 @safe unittest 1292 { 1293 auto arr = new size_t[4]; 1294 1295 arr.each!"a=i"(); 1296 assert(arr == [0, 1, 2, 3]); 1297 1298 arr.each!((i, ref e) => e = i * 2); 1299 assert(arr == [0, 2, 4, 6]); 1300 } 1301 1302 /// opApply iterators work as well 1303 @system unittest 1304 { 1305 static class S 1306 { 1307 int x; 1308 int opApply(scope int delegate(ref int _x) dg) { return dg(x); } 1309 } 1310 1311 auto s = new S; 1312 s.each!"a++"; 1313 assert(s.x == 1); 1314 } 1315 1316 // binary foreach with two ref args 1317 @system unittest 1318 { 1319 import std.range : lockstep; 1320 1321 auto a = [ 1, 2, 3 ]; 1322 auto b = [ 2, 3, 4 ]; 1323 1324 a.lockstep(b).each!((ref x, ref y) { ++x; ++y; }); 1325 1326 assert(a == [ 2, 3, 4 ]); 1327 assert(b == [ 3, 4, 5 ]); 1328 } 1329 1330 // https://issues.dlang.org/show_bug.cgi?id=15358 1331 // application of `each` with >2 args (opApply) 1332 @system unittest 1333 { 1334 import std.range : lockstep; 1335 auto a = [0,1,2]; 1336 auto b = [3,4,5]; 1337 auto c = [6,7,8]; 1338 1339 lockstep(a, b, c).each!((ref x, ref y, ref z) { ++x; ++y; ++z; }); 1340 1341 assert(a == [1,2,3]); 1342 assert(b == [4,5,6]); 1343 assert(c == [7,8,9]); 1344 } 1345 1346 // https://issues.dlang.org/show_bug.cgi?id=15358 1347 // application of `each` with >2 args (range interface) 1348 @safe unittest 1349 { 1350 import std.range : zip; 1351 auto a = [0,1,2]; 1352 auto b = [3,4,5]; 1353 auto c = [6,7,8]; 1354 1355 int[] res; 1356 1357 zip(a, b, c).each!((x, y, z) { res ~= x + y + z; }); 1358 1359 assert(res == [9, 12, 15]); 1360 } 1361 1362 // https://issues.dlang.org/show_bug.cgi?id=16255 1363 // `each` on opApply doesn't support ref 1364 @safe unittest 1365 { 1366 int[] dynamicArray = [1, 2, 3, 4, 5]; 1367 int[5] staticArray = [1, 2, 3, 4, 5]; 1368 1369 dynamicArray.each!((ref x) => x++); 1370 assert(dynamicArray == [2, 3, 4, 5, 6]); 1371 1372 staticArray.each!((ref x) => x++); 1373 assert(staticArray == [2, 3, 4, 5, 6]); 1374 1375 staticArray[].each!((ref x) => x++); 1376 assert(staticArray == [3, 4, 5, 6, 7]); 1377 } 1378 1379 // https://issues.dlang.org/show_bug.cgi?id=16255 1380 // `each` on opApply doesn't support ref 1381 @system unittest 1382 { 1383 struct S 1384 { 1385 int x; 1386 int opApply(int delegate(ref int _x) dg) { return dg(x); } 1387 } 1388 1389 S s; 1390 foreach (ref a; s) ++a; 1391 assert(s.x == 1); 1392 s.each!"++a"; 1393 assert(s.x == 2); 1394 } 1395 1396 // https://issues.dlang.org/show_bug.cgi?id=15357 1397 // `each` should behave similar to foreach 1398 @safe unittest 1399 { 1400 import std.range : iota; 1401 1402 auto arr = [1, 2, 3, 4]; 1403 1404 // 1 ref parameter 1405 arr.each!((ref e) => e = 0); 1406 assert(arr.sum == 0); 1407 1408 // 1 ref parameter and index 1409 arr.each!((i, ref e) => e = cast(int) i); 1410 assert(arr.sum == 4.iota.sum); 1411 } 1412 1413 // https://issues.dlang.org/show_bug.cgi?id=15357 1414 // `each` should behave similar to foreach 1415 @system unittest 1416 { 1417 import std.range : iota, lockstep; 1418 1419 // 2 ref parameters and index 1420 auto arrA = [1, 2, 3, 4]; 1421 auto arrB = [5, 6, 7, 8]; 1422 lockstep(arrA, arrB).each!((ref a, ref b) { 1423 a = 0; 1424 b = 1; 1425 }); 1426 assert(arrA.sum == 0); 1427 assert(arrB.sum == 4); 1428 1429 // 3 ref parameters 1430 auto arrC = [3, 3, 3, 3]; 1431 lockstep(arrA, arrB, arrC).each!((ref a, ref b, ref c) { 1432 a = 1; 1433 b = 2; 1434 c = 3; 1435 }); 1436 assert(arrA.sum == 4); 1437 assert(arrB.sum == 8); 1438 assert(arrC.sum == 12); 1439 } 1440 1441 // https://issues.dlang.org/show_bug.cgi?id=15357 1442 // `each` should behave similar to foreach 1443 @system unittest 1444 { 1445 import std.range : lockstep; 1446 import std.typecons : Tuple; 1447 1448 auto a = "abc"; 1449 auto b = "def"; 1450 1451 // print each character with an index 1452 { 1453 alias Element = Tuple!(size_t, "index", dchar, "value"); 1454 Element[] rForeach, rEach; 1455 foreach (i, c ; a) rForeach ~= Element(i, c); 1456 a.each!((i, c) => rEach ~= Element(i, c)); 1457 assert(rForeach == rEach); 1458 assert(rForeach == [Element(0, 'a'), Element(1, 'b'), Element(2, 'c')]); 1459 } 1460 1461 // print pairs of characters 1462 { 1463 alias Element = Tuple!(dchar, "a", dchar, "b"); 1464 Element[] rForeach, rEach; 1465 foreach (c1, c2 ; a.lockstep(b)) rForeach ~= Element(c1, c2); 1466 a.lockstep(b).each!((c1, c2) => rEach ~= Element(c1, c2)); 1467 assert(rForeach == rEach); 1468 assert(rForeach == [Element('a', 'd'), Element('b', 'e'), Element('c', 'f')]); 1469 } 1470 } 1471 1472 // filter 1473 /** 1474 `filter!(predicate)(range)` returns a new range containing only elements `x` in `range` for 1475 which `predicate(x)` returns `true`. 1476 1477 The predicate is passed to $(REF unaryFun, std,functional), and can be either a string, or 1478 any callable that can be executed via `pred(element)`. 1479 1480 Params: 1481 predicate = Function to apply to each element of range 1482 1483 Returns: 1484 An input range that contains the filtered elements. If `range` is at least a forward range, the return value of `filter` 1485 will also be a forward range. 1486 1487 See_Also: 1488 $(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function)), 1489 $(REF filterBidirectional, std,algorithm,iteration) 1490 */ 1491 template filter(alias predicate) 1492 if (is(typeof(unaryFun!predicate))) 1493 { 1494 /** 1495 Params: 1496 range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 1497 of elements 1498 Returns: 1499 A range containing only elements `x` in `range` for 1500 which `predicate(x)` returns `true`. 1501 */ 1502 auto filter(Range)(Range range) 1503 if (isInputRange!(Unqual!Range)) 1504 { 1505 return FilterResult!(unaryFun!predicate, Range)(range); 1506 } 1507 } 1508 1509 /// 1510 @safe unittest 1511 { 1512 import std.algorithm.comparison : equal; 1513 import std.math.operations : isClose; 1514 import std.range; 1515 1516 int[] arr = [ 1, 2, 3, 4, 5 ]; 1517 1518 // Filter below 3 1519 auto small = filter!(a => a < 3)(arr); 1520 assert(equal(small, [ 1, 2 ])); 1521 1522 // Filter again, but with Uniform Function Call Syntax (UFCS) 1523 auto sum = arr.filter!(a => a < 3); 1524 assert(equal(sum, [ 1, 2 ])); 1525 1526 // In combination with chain() to span multiple ranges 1527 int[] a = [ 3, -2, 400 ]; 1528 int[] b = [ 100, -101, 102 ]; 1529 auto r = chain(a, b).filter!(a => a > 0); 1530 assert(equal(r, [ 3, 400, 100, 102 ])); 1531 1532 // Mixing convertible types is fair game, too 1533 double[] c = [ 2.5, 3.0 ]; 1534 auto r1 = chain(c, a, b).filter!(a => cast(int) a != a); 1535 assert(isClose(r1, [ 2.5 ])); 1536 } 1537 1538 private struct FilterResult(alias pred, Range) 1539 { 1540 alias R = Unqual!Range; 1541 R _input; 1542 private bool _primed; 1543 1544 private void prime() 1545 { 1546 if (_primed) return; 1547 while (!_input.empty && !pred(_input.front)) 1548 { 1549 _input.popFront(); 1550 } 1551 _primed = true; 1552 } 1553 1554 this(R r) 1555 { 1556 _input = r; 1557 } 1558 1559 private this(R r, bool primed) 1560 { 1561 _input = r; 1562 _primed = primed; 1563 } 1564 1565 auto opSlice() { return this; } 1566 1567 static if (isInfinite!Range) 1568 { 1569 enum bool empty = false; 1570 } 1571 else 1572 { 1573 @property bool empty() { prime; return _input.empty; } 1574 } 1575 1576 void popFront() 1577 { 1578 prime; 1579 do 1580 { 1581 _input.popFront(); 1582 } while (!_input.empty && !pred(_input.front)); 1583 } 1584 1585 @property auto ref front() 1586 { 1587 prime; 1588 assert(!empty, "Attempting to fetch the front of an empty filter."); 1589 return _input.front; 1590 } 1591 1592 static if (isForwardRange!R) 1593 { 1594 @property auto save() 1595 { 1596 return typeof(this)(_input.save, _primed); 1597 } 1598 } 1599 } 1600 1601 @safe unittest 1602 { 1603 import std.algorithm.comparison : equal; 1604 import std.internal.test.dummyrange; 1605 import std.range; 1606 1607 auto shouldNotLoop4ever = repeat(1).filter!(x => x % 2 == 0); 1608 static assert(isInfinite!(typeof(shouldNotLoop4ever))); 1609 assert(!shouldNotLoop4ever.empty); 1610 1611 int[] a = [ 3, 4, 2 ]; 1612 auto r = filter!("a > 3")(a); 1613 static assert(isForwardRange!(typeof(r))); 1614 assert(equal(r, [ 4 ][])); 1615 1616 a = [ 1, 22, 3, 42, 5 ]; 1617 auto under10 = filter!("a < 10")(a); 1618 assert(equal(under10, [1, 3, 5][])); 1619 static assert(isForwardRange!(typeof(under10))); 1620 under10.front = 4; 1621 assert(equal(under10, [4, 3, 5][])); 1622 under10.front = 40; 1623 assert(equal(under10, [40, 3, 5][])); 1624 under10.front = 1; 1625 1626 auto infinite = filter!"a > 2"(repeat(3)); 1627 static assert(isInfinite!(typeof(infinite))); 1628 static assert(isForwardRange!(typeof(infinite))); 1629 assert(infinite.front == 3); 1630 1631 foreach (DummyType; AllDummyRanges) 1632 { 1633 DummyType d; 1634 auto f = filter!"a & 1"(d); 1635 assert(equal(f, [1,3,5,7,9])); 1636 1637 static if (isForwardRange!DummyType) 1638 { 1639 static assert(isForwardRange!(typeof(f))); 1640 } 1641 } 1642 1643 // With delegates 1644 int x = 10; 1645 int overX(int a) { return a > x; } 1646 typeof(filter!overX(a)) getFilter() 1647 { 1648 return filter!overX(a); 1649 } 1650 auto r1 = getFilter(); 1651 assert(equal(r1, [22, 42])); 1652 1653 // With chain 1654 auto nums = [0,1,2,3,4]; 1655 assert(equal(filter!overX(chain(a, nums)), [22, 42])); 1656 1657 // With copying of inner struct Filter to Map 1658 auto arr = [1,2,3,4,5]; 1659 auto m = map!"a + 1"(filter!"a < 4"(arr)); 1660 assert(equal(m, [2, 3, 4])); 1661 } 1662 1663 @safe unittest 1664 { 1665 import std.algorithm.comparison : equal; 1666 1667 int[] a = [ 3, 4 ]; 1668 const aConst = a; 1669 auto r = filter!("a > 3")(aConst); 1670 assert(equal(r, [ 4 ][])); 1671 1672 a = [ 1, 22, 3, 42, 5 ]; 1673 auto under10 = filter!("a < 10")(a); 1674 assert(equal(under10, [1, 3, 5][])); 1675 assert(equal(under10.save, [1, 3, 5][])); 1676 assert(equal(under10.save, under10)); 1677 } 1678 1679 @safe unittest 1680 { 1681 import std.algorithm.comparison : equal; 1682 import std.functional : compose, pipe; 1683 1684 assert(equal(compose!(map!"2 * a", filter!"a & 1")([1,2,3,4,5]), 1685 [2,6,10])); 1686 assert(equal(pipe!(filter!"a & 1", map!"2 * a")([1,2,3,4,5]), 1687 [2,6,10])); 1688 } 1689 1690 @safe unittest 1691 { 1692 import std.algorithm.comparison : equal; 1693 1694 int x = 10; 1695 int underX(int a) { return a < x; } 1696 const(int)[] list = [ 1, 2, 10, 11, 3, 4 ]; 1697 assert(equal(filter!underX(list), [ 1, 2, 3, 4 ])); 1698 } 1699 1700 // https://issues.dlang.org/show_bug.cgi?id=19823 1701 @safe unittest 1702 { 1703 import std.algorithm.comparison : equal; 1704 import std.range : dropOne; 1705 1706 auto a = [1, 2, 3, 4]; 1707 assert(a.filter!(a => a != 1).dropOne.equal([3, 4])); 1708 assert(a.filter!(a => a != 2).dropOne.equal([3, 4])); 1709 assert(a.filter!(a => a != 3).dropOne.equal([2, 4])); 1710 assert(a.filter!(a => a != 4).dropOne.equal([2, 3])); 1711 assert(a.filter!(a => a == 1).dropOne.empty); 1712 assert(a.filter!(a => a == 2).dropOne.empty); 1713 assert(a.filter!(a => a == 3).dropOne.empty); 1714 assert(a.filter!(a => a == 4).dropOne.empty); 1715 } 1716 1717 /** 1718 * Similar to `filter`, except it defines a 1719 * $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives). 1720 * There is a speed disadvantage - the constructor spends time 1721 * finding the last element in the range that satisfies the filtering 1722 * condition (in addition to finding the first one). The advantage is 1723 * that the filtered range can be spanned from both directions. Also, 1724 * $(REF retro, std,range) can be applied against the filtered range. 1725 * 1726 * The predicate is passed to $(REF unaryFun, std,functional), and can either 1727 * accept a string, or any callable that can be executed via `pred(element)`. 1728 * 1729 * Params: 1730 * pred = Function to apply to each element of range 1731 */ 1732 template filterBidirectional(alias pred) 1733 { 1734 /** 1735 Params: 1736 r = Bidirectional range of elements 1737 Returns: 1738 A range containing only the elements in `r` for which `pred` returns `true`. 1739 */ 1740 auto filterBidirectional(Range)(Range r) 1741 if (isBidirectionalRange!(Unqual!Range)) 1742 { 1743 return FilterBidiResult!(unaryFun!pred, Range)(r); 1744 } 1745 } 1746 1747 /// 1748 @safe unittest 1749 { 1750 import std.algorithm.comparison : equal; 1751 import std.range; 1752 1753 int[] arr = [ 1, 2, 3, 4, 5 ]; 1754 auto small = filterBidirectional!("a < 3")(arr); 1755 static assert(isBidirectionalRange!(typeof(small))); 1756 assert(small.back == 2); 1757 assert(equal(small, [ 1, 2 ])); 1758 assert(equal(retro(small), [ 2, 1 ])); 1759 // In combination with chain() to span multiple ranges 1760 int[] a = [ 3, -2, 400 ]; 1761 int[] b = [ 100, -101, 102 ]; 1762 auto r = filterBidirectional!("a > 0")(chain(a, b)); 1763 assert(r.back == 102); 1764 } 1765 1766 private struct FilterBidiResult(alias pred, Range) 1767 { 1768 alias R = Unqual!Range; 1769 R _input; 1770 1771 this(R r) 1772 { 1773 _input = r; 1774 while (!_input.empty && !pred(_input.front)) _input.popFront(); 1775 while (!_input.empty && !pred(_input.back)) _input.popBack(); 1776 } 1777 1778 @property bool empty() { return _input.empty; } 1779 1780 void popFront() 1781 { 1782 do 1783 { 1784 _input.popFront(); 1785 } while (!_input.empty && !pred(_input.front)); 1786 } 1787 1788 @property auto ref front() 1789 { 1790 assert(!empty, "Attempting to fetch the front of an empty filterBidirectional."); 1791 return _input.front; 1792 } 1793 1794 void popBack() 1795 { 1796 do 1797 { 1798 _input.popBack(); 1799 } while (!_input.empty && !pred(_input.back)); 1800 } 1801 1802 @property auto ref back() 1803 { 1804 assert(!empty, "Attempting to fetch the back of an empty filterBidirectional."); 1805 return _input.back; 1806 } 1807 1808 @property auto save() 1809 { 1810 return typeof(this)(_input.save); 1811 } 1812 } 1813 1814 /** 1815 Groups consecutively equivalent elements into a single tuple of the element and 1816 the number of its repetitions. 1817 1818 Similarly to `uniq`, `group` produces a range that iterates over unique 1819 consecutive elements of the given range. Each element of this range is a tuple 1820 of the element and the number of times it is repeated in the original range. 1821 Equivalence of elements is assessed by using the predicate `pred`, which 1822 defaults to `"a == b"`. The predicate is passed to $(REF binaryFun, std,functional), 1823 and can either accept a string, or any callable that can be executed via 1824 `pred(element, element)`. 1825 1826 Params: 1827 pred = Binary predicate for determining equivalence of two elements. 1828 R = The range type 1829 r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to 1830 iterate over. 1831 1832 Returns: A range of elements of type `Tuple!(ElementType!R, uint)`, 1833 representing each consecutively unique element and its respective number of 1834 occurrences in that run. This will be an input range if `R` is an input 1835 range, and a forward range in all other cases. 1836 1837 See_Also: $(LREF chunkBy), which chunks an input range into subranges 1838 of equivalent adjacent elements. 1839 */ 1840 Group!(pred, Range) group(alias pred = "a == b", Range)(Range r) 1841 { 1842 return typeof(return)(r); 1843 } 1844 1845 /// ditto 1846 struct Group(alias pred, R) 1847 if (isInputRange!R) 1848 { 1849 import std.typecons : Rebindable, tuple, Tuple; 1850 1851 private alias comp = binaryFun!pred; 1852 1853 private alias E = ElementType!R; 1854 static if ((is(E == class) || is(E == interface)) && 1855 (is(E == const) || is(E == immutable))) 1856 { 1857 private alias MutableE = Rebindable!E; 1858 } 1859 else static if (is(E : Unqual!E)) 1860 { 1861 private alias MutableE = Unqual!E; 1862 } 1863 else 1864 { 1865 private alias MutableE = E; 1866 } 1867 1868 private R _input; 1869 private Tuple!(MutableE, uint) _current; 1870 1871 /// 1872 this(R input) 1873 { 1874 _input = input; 1875 if (!_input.empty) popFront(); 1876 } 1877 1878 private this(R input, Tuple!(MutableE, uint) current) 1879 { 1880 _input = input; 1881 _current = current; 1882 } 1883 1884 /// 1885 void popFront() 1886 { 1887 if (_input.empty) 1888 { 1889 _current[1] = 0; 1890 } 1891 else 1892 { 1893 _current = tuple(_input.front, 1u); 1894 _input.popFront(); 1895 while (!_input.empty && comp(_current[0], _input.front)) 1896 { 1897 ++_current[1]; 1898 _input.popFront(); 1899 } 1900 } 1901 } 1902 1903 static if (isInfinite!R) 1904 { 1905 /// 1906 enum bool empty = false; // Propagate infiniteness. 1907 } 1908 else 1909 { 1910 /// 1911 @property bool empty() 1912 { 1913 return _current[1] == 0; 1914 } 1915 } 1916 1917 /// Returns: the front of the range 1918 @property auto ref front() 1919 { 1920 assert(!empty, "Attempting to fetch the front of an empty Group."); 1921 return _current; 1922 } 1923 1924 static if (isForwardRange!R) 1925 { 1926 /// 1927 @property typeof(this) save() 1928 { 1929 return Group(_input.save, _current); 1930 } 1931 } 1932 } 1933 1934 /// 1935 @safe unittest 1936 { 1937 import std.algorithm.comparison : equal; 1938 import std.typecons : tuple, Tuple; 1939 1940 int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; 1941 assert(equal(group(arr), [ tuple(1, 1u), tuple(2, 4u), tuple(3, 1u), 1942 tuple(4, 3u), tuple(5, 1u) ][])); 1943 } 1944 1945 /** 1946 * Using group, an associative array can be easily generated with the count of each 1947 * unique element in the range. 1948 */ 1949 @safe unittest 1950 { 1951 import std.algorithm.sorting : sort; 1952 import std.array : assocArray; 1953 1954 uint[string] result; 1955 auto range = ["a", "b", "a", "c", "b", "c", "c", "d", "e"]; 1956 result = range.sort!((a, b) => a < b) 1957 .group 1958 .assocArray; 1959 1960 assert(result == ["a": 2U, "b": 2U, "c": 3U, "d": 1U, "e": 1U]); 1961 } 1962 1963 @safe unittest 1964 { 1965 import std.algorithm.comparison : equal; 1966 import std.internal.test.dummyrange; 1967 import std.typecons : tuple, Tuple; 1968 1969 int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; 1970 assert(equal(group(arr), [ tuple(1, 1u), tuple(2, 4u), tuple(3, 1u), 1971 tuple(4, 3u), tuple(5, 1u) ][])); 1972 static assert(isForwardRange!(typeof(group(arr)))); 1973 1974 foreach (DummyType; AllDummyRanges) 1975 { 1976 DummyType d; 1977 auto g = group(d); 1978 1979 static assert(d.rt == RangeType.Input || isForwardRange!(typeof(g))); 1980 1981 assert(equal(g, [tuple(1, 1u), tuple(2, 1u), tuple(3, 1u), tuple(4, 1u), 1982 tuple(5, 1u), tuple(6, 1u), tuple(7, 1u), tuple(8, 1u), 1983 tuple(9, 1u), tuple(10, 1u)])); 1984 } 1985 } 1986 1987 @safe unittest 1988 { 1989 import std.algorithm.comparison : equal; 1990 import std.typecons : tuple; 1991 1992 // https://issues.dlang.org/show_bug.cgi?id=13857 1993 immutable(int)[] a1 = [1,1,2,2,2,3,4,4,5,6,6,7,8,9,9,9]; 1994 auto g1 = group(a1); 1995 assert(equal(g1, [ tuple(1, 2u), tuple(2, 3u), tuple(3, 1u), 1996 tuple(4, 2u), tuple(5, 1u), tuple(6, 2u), 1997 tuple(7, 1u), tuple(8, 1u), tuple(9, 3u) 1998 ])); 1999 2000 // https://issues.dlang.org/show_bug.cgi?id=13162 2001 immutable(ubyte)[] a2 = [1, 1, 1, 0, 0, 0]; 2002 auto g2 = a2.group; 2003 assert(equal(g2, [ tuple(1, 3u), tuple(0, 3u) ])); 2004 2005 // https://issues.dlang.org/show_bug.cgi?id=10104 2006 const a3 = [1, 1, 2, 2]; 2007 auto g3 = a3.group; 2008 assert(equal(g3, [ tuple(1, 2u), tuple(2, 2u) ])); 2009 2010 interface I {} 2011 static class C : I { override size_t toHash() const nothrow @safe { return 0; } } 2012 const C[] a4 = [new const C()]; 2013 auto g4 = a4.group!"a is b"; 2014 assert(g4.front[1] == 1); 2015 2016 immutable I[] a5 = [new immutable C()]; 2017 auto g5 = a5.group!"a is b"; 2018 assert(g5.front[1] == 1); 2019 2020 const(int[][]) a6 = [[1], [1]]; 2021 auto g6 = a6.group; 2022 assert(equal(g6.front[0], [1])); 2023 } 2024 2025 @safe unittest 2026 { 2027 import std.algorithm.comparison : equal; 2028 import std.typecons : tuple; 2029 2030 int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; 2031 auto r = arr.group; 2032 assert(r.equal([ tuple(1,1u), tuple(2, 4u), tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ])); 2033 r.popFront; 2034 assert(r.equal([ tuple(2, 4u), tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ])); 2035 auto s = r.save; 2036 r.popFrontN(2); 2037 assert(r.equal([ tuple(4, 3u), tuple(5, 1u) ])); 2038 assert(s.equal([ tuple(2, 4u), tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ])); 2039 s.popFront; 2040 auto t = s.save; 2041 r.popFront; 2042 s.popFront; 2043 assert(r.equal([ tuple(5, 1u) ])); 2044 assert(s.equal([ tuple(4, 3u), tuple(5, 1u) ])); 2045 assert(t.equal([ tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ])); 2046 } 2047 2048 // https://issues.dlang.org/show_bug.cgi?id=18657 2049 pure @safe unittest 2050 { 2051 import std.algorithm.comparison : equal; 2052 import std.range : refRange; 2053 string s = "foo"; 2054 auto r = refRange(&s).group; 2055 assert(equal(r.save, "foo".group)); 2056 assert(equal(r, "foo".group)); 2057 } 2058 2059 // Used by implementation of chunkBy for non-forward input ranges. 2060 private struct ChunkByChunkImpl(alias pred, Range) 2061 if (isInputRange!Range && !isForwardRange!Range) 2062 { 2063 alias fun = binaryFun!pred; 2064 2065 private Range *r; 2066 private ElementType!Range prev; 2067 2068 this(ref Range range, ElementType!Range _prev) 2069 { 2070 r = ⦥ 2071 prev = _prev; 2072 } 2073 2074 @property bool empty() 2075 { 2076 return r.empty || !fun(prev, r.front); 2077 } 2078 2079 @property ElementType!Range front() 2080 { 2081 assert(!empty, "Attempting to fetch the front of an empty chunkBy chunk."); 2082 return r.front; 2083 } 2084 2085 void popFront() 2086 { 2087 assert(!empty, "Attempting to popFront an empty chunkBy chunk."); 2088 r.popFront(); 2089 } 2090 } 2091 2092 private template ChunkByImplIsUnary(alias pred, Range) 2093 { 2094 alias e = lvalueOf!(ElementType!Range); 2095 2096 static if (is(typeof(binaryFun!pred(e, e)) : bool)) 2097 enum ChunkByImplIsUnary = false; 2098 else static if (is(typeof(unaryFun!pred(e) == unaryFun!pred(e)) : bool)) 2099 enum ChunkByImplIsUnary = true; 2100 else 2101 static assert(0, "chunkBy expects either a binary predicate or "~ 2102 "a unary predicate on range elements of type: "~ 2103 ElementType!Range.stringof); 2104 } 2105 2106 // Implementation of chunkBy for non-forward input ranges. 2107 private struct ChunkByImpl(alias pred, Range) 2108 if (isInputRange!Range && !isForwardRange!Range) 2109 { 2110 enum bool isUnary = ChunkByImplIsUnary!(pred, Range); 2111 2112 static if (isUnary) 2113 alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b)); 2114 else 2115 alias eq = binaryFun!pred; 2116 2117 private Range r; 2118 private ElementType!Range _prev; 2119 private bool openChunk = false; 2120 2121 this(Range _r) 2122 { 2123 r = _r; 2124 if (!empty) 2125 { 2126 // Check reflexivity if predicate is claimed to be an equivalence 2127 // relation. 2128 assert(eq(r.front, r.front), 2129 "predicate is not reflexive"); 2130 2131 // _prev's type may be a nested struct, so must be initialized 2132 // directly in the constructor (cannot call savePred()). 2133 _prev = r.front; 2134 } 2135 else 2136 { 2137 // We won't use _prev, but must be initialized. 2138 _prev = typeof(_prev).init; 2139 } 2140 } 2141 @property bool empty() { return r.empty && !openChunk; } 2142 2143 @property auto front() 2144 { 2145 assert(!empty, "Attempting to fetch the front of an empty chunkBy."); 2146 openChunk = true; 2147 static if (isUnary) 2148 { 2149 import std.typecons : tuple; 2150 return tuple(unaryFun!pred(_prev), 2151 ChunkByChunkImpl!(eq, Range)(r, _prev)); 2152 } 2153 else 2154 { 2155 return ChunkByChunkImpl!(eq, Range)(r, _prev); 2156 } 2157 } 2158 2159 void popFront() 2160 { 2161 assert(!empty, "Attempting to popFront an empty chunkBy."); 2162 openChunk = false; 2163 while (!r.empty) 2164 { 2165 if (!eq(_prev, r.front)) 2166 { 2167 _prev = r.front; 2168 break; 2169 } 2170 r.popFront(); 2171 } 2172 } 2173 } 2174 // Outer range for forward range version of chunkBy 2175 private struct ChunkByOuter(Range, bool eqEquivalenceAssured) 2176 { 2177 size_t groupNum; 2178 Range current; 2179 Range next; 2180 static if (!eqEquivalenceAssured) 2181 { 2182 bool nextUpdated; 2183 } 2184 } 2185 2186 // Inner range for forward range version of chunkBy 2187 private struct ChunkByGroup(alias eq, Range, bool eqEquivalenceAssured) 2188 { 2189 import std.typecons : RefCounted; 2190 2191 alias OuterRange = ChunkByOuter!(Range, eqEquivalenceAssured); 2192 2193 private size_t groupNum; 2194 static if (eqEquivalenceAssured) 2195 { 2196 private Range start; 2197 } 2198 private Range current; 2199 2200 // using union prevents RefCounted destructor from propagating @system to 2201 // user code 2202 union { private RefCounted!(OuterRange) mothership; } 2203 private @trusted ref cargo() { return mothership.refCountedPayload; } 2204 2205 private this(ref RefCounted!(OuterRange) origin) 2206 { 2207 () @trusted { mothership = origin; }(); 2208 groupNum = cargo.groupNum; 2209 current = cargo.current.save; 2210 assert(!current.empty, "Passed range 'r' must not be empty"); 2211 2212 static if (eqEquivalenceAssured) 2213 { 2214 start = cargo.current.save; 2215 2216 // Check for reflexivity. 2217 assert(eq(start.front, current.front), 2218 "predicate is not reflexive"); 2219 } 2220 } 2221 2222 // Cannot be a copy constructor due to https://issues.dlang.org/show_bug.cgi?id=22239 2223 this(this) scope @trusted 2224 { 2225 import core.lifetime : emplace; 2226 // since mothership has to be in a union, we have to manually trigger 2227 // an increment to the reference count. 2228 auto temp = mothership; 2229 mothership = temp; 2230 2231 // prevents the reference count from falling back with brute force 2232 emplace(&temp); 2233 } 2234 2235 @property bool empty() { return groupNum == size_t.max; } 2236 @property auto ref front() { return current.front; } 2237 2238 void popFront() 2239 { 2240 static if (!eqEquivalenceAssured) 2241 { 2242 auto prevElement = current.front; 2243 } 2244 2245 current.popFront(); 2246 2247 static if (eqEquivalenceAssured) 2248 { 2249 //this requires transitivity from the predicate. 2250 immutable nowEmpty = current.empty || !eq(start.front, current.front); 2251 } 2252 else 2253 { 2254 immutable nowEmpty = current.empty || !eq(prevElement, current.front); 2255 } 2256 2257 2258 if (nowEmpty) 2259 { 2260 if (groupNum == cargo.groupNum) 2261 { 2262 // If parent range hasn't moved on yet, help it along by 2263 // saving location of start of next Group. 2264 cargo.next = current.save; 2265 static if (!eqEquivalenceAssured) 2266 { 2267 cargo.nextUpdated = true; 2268 } 2269 } 2270 2271 groupNum = size_t.max; 2272 } 2273 } 2274 2275 @property auto save() 2276 { 2277 auto copy = this; 2278 copy.current = current.save; 2279 return copy; 2280 } 2281 2282 @trusted ~this() 2283 { 2284 mothership.destroy; 2285 } 2286 } 2287 2288 private enum GroupingOpType{binaryEquivalent, binaryAny, unary} 2289 2290 // Single-pass implementation of chunkBy for forward ranges. 2291 private struct ChunkByImpl(alias pred, alias eq, GroupingOpType opType, Range) 2292 if (isForwardRange!Range) 2293 { 2294 import std.typecons : RefCounted; 2295 2296 enum bool eqEquivalenceAssured = opType != GroupingOpType.binaryAny; 2297 alias OuterRange = ChunkByOuter!(Range, eqEquivalenceAssured); 2298 alias InnerRange = ChunkByGroup!(eq, Range, eqEquivalenceAssured); 2299 2300 static assert(isForwardRange!InnerRange); 2301 2302 // using union prevents RefCounted destructor from propagating @system to 2303 // user code 2304 union { private RefCounted!OuterRange _impl; } 2305 private @trusted ref impl() { return _impl; } 2306 private @trusted ref implPL() { return _impl.refCountedPayload; } 2307 2308 this(Range r) 2309 { 2310 import core.lifetime : move; 2311 2312 auto savedR = r.save; 2313 2314 static if (eqEquivalenceAssured) () @trusted 2315 { 2316 _impl = RefCounted!OuterRange(0, r, savedR.move); 2317 }(); 2318 else () @trusted 2319 { 2320 _impl = RefCounted!OuterRange(0, r, savedR.move, false); 2321 }(); 2322 } 2323 2324 // Cannot be a copy constructor due to https://issues.dlang.org/show_bug.cgi?id=22239 2325 this(this) scope @trusted 2326 { 2327 import core.lifetime : emplace; 2328 // since _impl has to be in a union, we have to manually trigger 2329 // an increment to the reference count. 2330 auto temp = _impl; 2331 _impl = temp; 2332 2333 // prevents the reference count from falling back with brute force 2334 emplace(&temp); 2335 } 2336 2337 @property bool empty() { return implPL.current.empty; } 2338 2339 static if (opType == GroupingOpType.unary) @property auto front() 2340 { 2341 import std.typecons : tuple; 2342 2343 return tuple(unaryFun!pred(implPL.current.front), InnerRange(impl)); 2344 } 2345 else @property auto front() 2346 { 2347 return InnerRange(impl); 2348 } 2349 2350 static if (eqEquivalenceAssured) void popFront() 2351 { 2352 // Scan for next group. If we're lucky, one of our Groups would have 2353 // already set .next to the start of the next group, in which case the 2354 // loop is skipped. 2355 while (!implPL.next.empty && eq(implPL.current.front, implPL.next.front)) 2356 { 2357 implPL.next.popFront(); 2358 } 2359 2360 implPL.current = implPL.next.save; 2361 2362 // Indicate to any remaining Groups that we have moved on. 2363 implPL.groupNum++; 2364 } 2365 else void popFront() 2366 { 2367 if (implPL.nextUpdated) 2368 { 2369 implPL.current = implPL.next.save; 2370 } 2371 else while (true) 2372 { 2373 auto prevElement = implPL.current.front; 2374 implPL.current.popFront(); 2375 if (implPL.current.empty) break; 2376 if (!eq(prevElement, implPL.current.front)) break; 2377 } 2378 2379 implPL.nextUpdated = false; 2380 // Indicate to any remaining Groups that we have moved on. 2381 implPL.groupNum++; 2382 } 2383 2384 @property auto save() 2385 { 2386 // Note: the new copy of the range will be detached from any existing 2387 // satellite Groups, and will not benefit from the .next acceleration. 2388 return typeof(this)(implPL.current.save); 2389 } 2390 2391 static assert(isForwardRange!(typeof(this)), typeof(this).stringof 2392 ~ " must be a forward range"); 2393 2394 @trusted ~this() 2395 { 2396 _impl.destroy; 2397 } 2398 } 2399 2400 //Test for https://issues.dlang.org/show_bug.cgi?id=14909 2401 @safe unittest 2402 { 2403 import std.algorithm.comparison : equal; 2404 import std.typecons : tuple; 2405 import std.stdio; 2406 auto n = 3; 2407 auto s = [1,2,3].chunkBy!(a => a+n); 2408 auto t = s.save.map!(x=>x[0]); 2409 auto u = s.map!(x=>x[1]); 2410 assert(t.equal([4,5,6])); 2411 assert(u.equal!equal([[1],[2],[3]])); 2412 } 2413 2414 //Testing inferring @system correctly 2415 @safe unittest 2416 { 2417 struct DeadlySave 2418 { 2419 int front; 2420 @safe void popFront(){front++;} 2421 @safe bool empty(){return front >= 5;} 2422 @system auto save(){return this;} 2423 } 2424 2425 auto test1() 2426 { 2427 DeadlySave src; 2428 return src.walkLength; 2429 2430 } 2431 2432 auto test2() 2433 { 2434 DeadlySave src; 2435 return src.chunkBy!((a,b) => a % 2 == b % 2).walkLength; 2436 } 2437 2438 static assert(isSafe!test1); 2439 static assert(!isSafe!test2); 2440 } 2441 2442 //Test for https://issues.dlang.org/show_bug.cgi?id=18751 2443 @safe unittest 2444 { 2445 import std.algorithm.comparison : equal; 2446 2447 string[] data = [ "abc", "abc", "def" ]; 2448 int[] indices = [ 0, 1, 2 ]; 2449 2450 auto chunks = indices.chunkBy!((i, j) => data[i] == data[j]); 2451 assert(chunks.equal!equal([ [ 0, 1 ], [ 2 ] ])); 2452 } 2453 2454 //Additional test for fix for issues 14909 and 18751 2455 @safe unittest 2456 { 2457 import std.algorithm.comparison : equal; 2458 auto v = [2,4,8,3,6,9,1,5,7]; 2459 auto i = 2; 2460 assert(v.chunkBy!((a,b) => a % i == b % i).equal!equal([[2,4,8],[3],[6],[9,1,5,7]])); 2461 } 2462 2463 @system unittest 2464 { 2465 import std.algorithm.comparison : equal; 2466 2467 size_t popCount = 0; 2468 static class RefFwdRange 2469 { 2470 int[] impl; 2471 size_t* pcount; 2472 2473 @safe nothrow: 2474 2475 this(int[] data, size_t* pcount) { impl = data; this.pcount = pcount; } 2476 @property bool empty() { return impl.empty; } 2477 @property auto ref front() { return impl.front; } 2478 void popFront() 2479 { 2480 impl.popFront(); 2481 (*pcount)++; 2482 } 2483 @property auto save() { return new RefFwdRange(impl, pcount); } 2484 } 2485 static assert(isForwardRange!RefFwdRange); 2486 2487 auto testdata = new RefFwdRange([1, 3, 5, 2, 4, 7, 6, 8, 9], &popCount); 2488 auto groups = testdata.chunkBy!((a,b) => (a % 2) == (b % 2)); 2489 auto outerSave1 = groups.save; 2490 2491 // Sanity test 2492 assert(groups.equal!equal([[1, 3, 5], [2, 4], [7], [6, 8], [9]])); 2493 assert(groups.empty); 2494 2495 // Performance test for single-traversal use case: popFront should not have 2496 // been called more times than there are elements if we traversed the 2497 // segmented range exactly once. 2498 assert(popCount == 9); 2499 2500 // Outer range .save test 2501 groups = outerSave1.save; 2502 assert(!groups.empty); 2503 2504 // Inner range .save test 2505 auto grp1 = groups.front.save; 2506 auto grp1b = grp1.save; 2507 assert(grp1b.equal([1, 3, 5])); 2508 assert(grp1.save.equal([1, 3, 5])); 2509 2510 // Inner range should remain consistent after outer range has moved on. 2511 groups.popFront(); 2512 assert(grp1.save.equal([1, 3, 5])); 2513 2514 // Inner range should not be affected by subsequent inner ranges. 2515 assert(groups.front.equal([2, 4])); 2516 assert(grp1.save.equal([1, 3, 5])); 2517 } 2518 2519 /** 2520 * Chunks an input range into subranges of equivalent adjacent elements. 2521 * In other languages this is often called `partitionBy`, `groupBy` 2522 * or `sliceWhen`. 2523 * 2524 * Equivalence is defined by the predicate `pred`, which can be either 2525 * binary, which is passed to $(REF binaryFun, std,functional), or unary, which is 2526 * passed to $(REF unaryFun, std,functional). In the binary form, two range elements 2527 * `a` and `b` are considered equivalent if `pred(a,b)` is true. In 2528 * unary form, two elements are considered equivalent if `pred(a) == pred(b)` 2529 * is true. 2530 * 2531 * This predicate must be an equivalence relation, that is, it must be 2532 * reflexive (`pred(x,x)` is always true), symmetric 2533 * (`pred(x,y) == pred(y,x)`), and transitive (`pred(x,y) && pred(y,z)` 2534 * implies `pred(x,z)`). If this is not the case, the range returned by 2535 * chunkBy may assert at runtime or behave erratically. Use $(LREF splitWhen) 2536 * if you want to chunk by a predicate that is not an equivalence relation. 2537 * 2538 * Params: 2539 * pred = Predicate for determining equivalence. 2540 * r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be chunked. 2541 * 2542 * Returns: With a binary predicate, a range of ranges is returned in which 2543 * all elements in a given subrange are equivalent under the given predicate. 2544 * With a unary predicate, a range of tuples is returned, with the tuple 2545 * consisting of the result of the unary predicate for each subrange, and the 2546 * subrange itself. Copying the range currently has reference semantics, but this may 2547 * change in the future. 2548 * 2549 * Notes: 2550 * 2551 * Equivalent elements separated by an intervening non-equivalent element will 2552 * appear in separate subranges; this function only considers adjacent 2553 * equivalence. Elements in the subranges will always appear in the same order 2554 * they appear in the original range. 2555 * 2556 * See_also: 2557 * $(LREF group), which collapses adjacent equivalent elements into a single 2558 * element. 2559 */ 2560 auto chunkBy(alias pred, Range)(Range r) 2561 if (isInputRange!Range) 2562 { 2563 static if (ChunkByImplIsUnary!(pred, Range)) 2564 { 2565 enum opType = GroupingOpType.unary; 2566 alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b)); 2567 } 2568 else 2569 { 2570 enum opType = GroupingOpType.binaryEquivalent; 2571 alias eq = binaryFun!pred; 2572 } 2573 static if (isForwardRange!Range) 2574 return ChunkByImpl!(pred, eq, opType, Range)(r); 2575 else 2576 return ChunkByImpl!(pred, Range)(r); 2577 } 2578 2579 /// Showing usage with binary predicate: 2580 @safe unittest 2581 { 2582 import std.algorithm.comparison : equal; 2583 2584 // Grouping by particular attribute of each element: 2585 auto data = [ 2586 [1, 1], 2587 [1, 2], 2588 [2, 2], 2589 [2, 3] 2590 ]; 2591 2592 auto r1 = data.chunkBy!((a,b) => a[0] == b[0]); 2593 assert(r1.equal!equal([ 2594 [[1, 1], [1, 2]], 2595 [[2, 2], [2, 3]] 2596 ])); 2597 2598 auto r2 = data.chunkBy!((a,b) => a[1] == b[1]); 2599 assert(r2.equal!equal([ 2600 [[1, 1]], 2601 [[1, 2], [2, 2]], 2602 [[2, 3]] 2603 ])); 2604 } 2605 2606 /// Showing usage with unary predicate: 2607 /* FIXME: pure nothrow*/ @safe unittest 2608 { 2609 import std.algorithm.comparison : equal; 2610 import std.range.primitives; 2611 import std.typecons : tuple; 2612 2613 // Grouping by particular attribute of each element: 2614 auto range = 2615 [ 2616 [1, 1], 2617 [1, 1], 2618 [1, 2], 2619 [2, 2], 2620 [2, 3], 2621 [2, 3], 2622 [3, 3] 2623 ]; 2624 2625 auto byX = chunkBy!(a => a[0])(range); 2626 auto expected1 = 2627 [ 2628 tuple(1, [[1, 1], [1, 1], [1, 2]]), 2629 tuple(2, [[2, 2], [2, 3], [2, 3]]), 2630 tuple(3, [[3, 3]]) 2631 ]; 2632 foreach (e; byX) 2633 { 2634 assert(!expected1.empty); 2635 assert(e[0] == expected1.front[0]); 2636 assert(e[1].equal(expected1.front[1])); 2637 expected1.popFront(); 2638 } 2639 2640 auto byY = chunkBy!(a => a[1])(range); 2641 auto expected2 = 2642 [ 2643 tuple(1, [[1, 1], [1, 1]]), 2644 tuple(2, [[1, 2], [2, 2]]), 2645 tuple(3, [[2, 3], [2, 3], [3, 3]]) 2646 ]; 2647 foreach (e; byY) 2648 { 2649 assert(!expected2.empty); 2650 assert(e[0] == expected2.front[0]); 2651 assert(e[1].equal(expected2.front[1])); 2652 expected2.popFront(); 2653 } 2654 } 2655 2656 /*FIXME: pure @safe nothrow*/ @system unittest 2657 { 2658 import std.algorithm.comparison : equal; 2659 import std.typecons : tuple; 2660 2661 struct Item { int x, y; } 2662 2663 // Force R to have only an input range API with reference semantics, so 2664 // that we're not unknowingly making use of array semantics outside of the 2665 // range API. 2666 class RefInputRange(R) 2667 { 2668 R data; 2669 this(R _data) pure @safe nothrow { data = _data; } 2670 @property bool empty() pure @safe nothrow { return data.empty; } 2671 @property auto front() pure @safe nothrow { assert(!empty); return data.front; } 2672 void popFront() pure @safe nothrow { assert(!empty); data.popFront(); } 2673 } 2674 auto refInputRange(R)(R range) { return new RefInputRange!R(range); } 2675 2676 // An input range API with value semantics. 2677 struct ValInputRange(R) 2678 { 2679 R data; 2680 this(R _data) pure @safe nothrow { data = _data; } 2681 @property bool empty() pure @safe nothrow { return data.empty; } 2682 @property auto front() pure @safe nothrow { assert(!empty); return data.front; } 2683 void popFront() pure @safe nothrow { assert(!empty); data.popFront(); } 2684 } 2685 auto valInputRange(R)(R range) { return ValInputRange!R(range); } 2686 2687 { 2688 auto arr = [ Item(1,2), Item(1,3), Item(2,3) ]; 2689 static assert(isForwardRange!(typeof(arr))); 2690 2691 auto byX = chunkBy!(a => a.x)(arr); 2692 static assert(isForwardRange!(typeof(byX))); 2693 2694 auto byX_subrange1 = byX.front[1].save; 2695 auto byX_subrange2 = byX.front[1].save; 2696 static assert(isForwardRange!(typeof(byX_subrange1))); 2697 static assert(isForwardRange!(typeof(byX_subrange2))); 2698 2699 byX.popFront(); 2700 assert(byX_subrange1.equal([ Item(1,2), Item(1,3) ])); 2701 byX_subrange1.popFront(); 2702 assert(byX_subrange1.equal([ Item(1,3) ])); 2703 assert(byX_subrange2.equal([ Item(1,2), Item(1,3) ])); 2704 2705 auto byY = chunkBy!(a => a.y)(arr); 2706 static assert(isForwardRange!(typeof(byY))); 2707 2708 auto byY2 = byY.save; 2709 static assert(is(typeof(byY) == typeof(byY2))); 2710 byY.popFront(); 2711 assert(byY.front[0] == 3); 2712 assert(byY.front[1].equal([ Item(1,3), Item(2,3) ])); 2713 assert(byY2.front[0] == 2); 2714 assert(byY2.front[1].equal([ Item(1,2) ])); 2715 } 2716 2717 // Test non-forward input ranges with reference semantics. 2718 { 2719 auto range = refInputRange([ Item(1,1), Item(1,2), Item(2,2) ]); 2720 auto byX = chunkBy!(a => a.x)(range); 2721 assert(byX.front[0] == 1); 2722 assert(byX.front[1].equal([ Item(1,1), Item(1,2) ])); 2723 byX.popFront(); 2724 assert(byX.front[0] == 2); 2725 assert(byX.front[1].equal([ Item(2,2) ])); 2726 byX.popFront(); 2727 assert(byX.empty); 2728 assert(range.empty); 2729 2730 range = refInputRange([ Item(1,1), Item(1,2), Item(2,2) ]); 2731 auto byY = chunkBy!(a => a.y)(range); 2732 assert(byY.front[0] == 1); 2733 assert(byY.front[1].equal([ Item(1,1) ])); 2734 byY.popFront(); 2735 assert(byY.front[0] == 2); 2736 assert(byY.front[1].equal([ Item(1,2), Item(2,2) ])); 2737 byY.popFront(); 2738 assert(byY.empty); 2739 assert(range.empty); 2740 } 2741 2742 // Test non-forward input ranges with value semantics. 2743 { 2744 auto range = valInputRange([ Item(1,1), Item(1,2), Item(2,2) ]); 2745 auto byX = chunkBy!(a => a.x)(range); 2746 assert(byX.front[0] == 1); 2747 assert(byX.front[1].equal([ Item(1,1), Item(1,2) ])); 2748 byX.popFront(); 2749 assert(byX.front[0] == 2); 2750 assert(byX.front[1].equal([ Item(2,2) ])); 2751 byX.popFront(); 2752 assert(byX.empty); 2753 assert(!range.empty); // Opposite of refInputRange test 2754 2755 range = valInputRange([ Item(1,1), Item(1,2), Item(2,2) ]); 2756 auto byY = chunkBy!(a => a.y)(range); 2757 assert(byY.front[0] == 1); 2758 assert(byY.front[1].equal([ Item(1,1) ])); 2759 byY.popFront(); 2760 assert(byY.front[0] == 2); 2761 assert(byY.front[1].equal([ Item(1,2), Item(2,2) ])); 2762 byY.popFront(); 2763 assert(byY.empty); 2764 assert(!range.empty); // Opposite of refInputRange test 2765 } 2766 2767 /* https://issues.dlang.org/show_bug.cgi?id=19532 2768 * General behavior of non-forward input ranges. 2769 * 2770 * - If the same chunk is retrieved multiple times via front, the separate chunk 2771 * instances refer to a shared range segment that advances as a single range. 2772 * - Emptying a chunk via popFront does not implicitly popFront the chunk off 2773 * main range. The chunk is still available via front, it is just empty. 2774 */ 2775 { 2776 import std.algorithm.comparison : equal; 2777 import core.exception : AssertError; 2778 import std.exception : assertThrown; 2779 2780 auto a = [[0, 0], [0, 1], 2781 [1, 2], [1, 3], [1, 4], 2782 [2, 5], [2, 6], 2783 [3, 7], 2784 [4, 8]]; 2785 2786 // Value input range 2787 { 2788 auto r = valInputRange(a).chunkBy!((a, b) => a[0] == b[0]); 2789 2790 size_t numChunks = 0; 2791 while (!r.empty) 2792 { 2793 ++numChunks; 2794 auto chunk = r.front; 2795 while (!chunk.empty) 2796 { 2797 assert(r.front.front[1] == chunk.front[1]); 2798 chunk.popFront; 2799 } 2800 assert(!r.empty); 2801 assert(r.front.empty); 2802 r.popFront; 2803 } 2804 2805 assert(numChunks == 5); 2806 2807 // Now front and popFront should assert. 2808 bool thrown = false; 2809 try r.front; 2810 catch (AssertError) thrown = true; 2811 assert(thrown); 2812 2813 thrown = false; 2814 try r.popFront; 2815 catch (AssertError) thrown = true; 2816 assert(thrown); 2817 } 2818 2819 // Reference input range 2820 { 2821 auto r = refInputRange(a).chunkBy!((a, b) => a[0] == b[0]); 2822 2823 size_t numChunks = 0; 2824 while (!r.empty) 2825 { 2826 ++numChunks; 2827 auto chunk = r.front; 2828 while (!chunk.empty) 2829 { 2830 assert(r.front.front[1] == chunk.front[1]); 2831 chunk.popFront; 2832 } 2833 assert(!r.empty); 2834 assert(r.front.empty); 2835 r.popFront; 2836 } 2837 2838 assert(numChunks == 5); 2839 2840 // Now front and popFront should assert. 2841 bool thrown = false; 2842 try r.front; 2843 catch (AssertError) thrown = true; 2844 assert(thrown); 2845 2846 thrown = false; 2847 try r.popFront; 2848 catch (AssertError) thrown = true; 2849 assert(thrown); 2850 } 2851 2852 // Ensure that starting with an empty range doesn't create an empty chunk. 2853 { 2854 int[] emptyRange = []; 2855 2856 auto r1 = valInputRange(emptyRange).chunkBy!((a, b) => a == b); 2857 auto r2 = refInputRange(emptyRange).chunkBy!((a, b) => a == b); 2858 2859 assert(r1.empty); 2860 assert(r2.empty); 2861 2862 bool thrown = false; 2863 try r1.front; 2864 catch (AssertError) thrown = true; 2865 assert(thrown); 2866 2867 thrown = false; 2868 try r1.popFront; 2869 catch (AssertError) thrown = true; 2870 assert(thrown); 2871 2872 thrown = false; 2873 try r2.front; 2874 catch (AssertError) thrown = true; 2875 assert(thrown); 2876 2877 thrown = false; 2878 try r2.popFront; 2879 catch (AssertError) thrown = true; 2880 assert(thrown); 2881 } 2882 } 2883 2884 // https://issues.dlang.org/show_bug.cgi?id=19532 - Using roundRobin/chunkBy 2885 { 2886 import std.algorithm.comparison : equal; 2887 import std.range : roundRobin; 2888 2889 auto a0 = [0, 1, 3, 6]; 2890 auto a1 = [0, 2, 4, 6, 7]; 2891 auto a2 = [1, 2, 4, 6, 8, 8, 9]; 2892 2893 auto expected = 2894 [[0, 0], [1, 1], [2, 2], [3], [4, 4], [6, 6, 6], [7], [8, 8], [9]]; 2895 2896 auto r1 = roundRobin(valInputRange(a0), valInputRange(a1), valInputRange(a2)) 2897 .chunkBy!((a, b) => a == b); 2898 assert(r1.equal!equal(expected)); 2899 2900 auto r2 = roundRobin(refInputRange(a0), refInputRange(a1), refInputRange(a2)) 2901 .chunkBy!((a, b) => a == b); 2902 assert(r2.equal!equal(expected)); 2903 2904 auto r3 = roundRobin(a0, a1, a2).chunkBy!((a, b) => a == b); 2905 assert(r3.equal!equal(expected)); 2906 } 2907 2908 // https://issues.dlang.org/show_bug.cgi?id=19532 - Using merge/chunkBy 2909 { 2910 import std.algorithm.comparison : equal; 2911 import std.algorithm.sorting : merge; 2912 2913 auto a0 = [2, 3, 5]; 2914 auto a1 = [2, 4, 5]; 2915 auto a2 = [1, 2, 4, 5]; 2916 2917 auto expected = [[1], [2, 2, 2], [3], [4, 4], [5, 5, 5]]; 2918 2919 auto r1 = merge(valInputRange(a0), valInputRange(a1), valInputRange(a2)) 2920 .chunkBy!((a, b) => a == b); 2921 assert(r1.equal!equal(expected)); 2922 2923 auto r2 = merge(refInputRange(a0), refInputRange(a1), refInputRange(a2)) 2924 .chunkBy!((a, b) => a == b); 2925 assert(r2.equal!equal(expected)); 2926 2927 auto r3 = merge(a0, a1, a2).chunkBy!((a, b) => a == b); 2928 assert(r3.equal!equal(expected)); 2929 } 2930 2931 // https://issues.dlang.org/show_bug.cgi?id=19532 - Using chunkBy/map-fold 2932 { 2933 import std.algorithm.comparison : equal; 2934 import std.algorithm.iteration : fold, map; 2935 2936 auto a = [0, 0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7, 8, 8, 9]; 2937 auto expected = [0, 3, 4, 6, 8, 5, 18, 7, 16, 9]; 2938 2939 auto r1 = a 2940 .chunkBy!((a, b) => a == b) 2941 .map!(c => c.fold!((a, b) => a + b)); 2942 assert(r1.equal(expected)); 2943 2944 auto r2 = valInputRange(a) 2945 .chunkBy!((a, b) => a == b) 2946 .map!(c => c.fold!((a, b) => a + b)); 2947 assert(r2.equal(expected)); 2948 2949 auto r3 = refInputRange(a) 2950 .chunkBy!((a, b) => a == b) 2951 .map!(c => c.fold!((a, b) => a + b)); 2952 assert(r3.equal(expected)); 2953 } 2954 2955 // https://issues.dlang.org/show_bug.cgi?id=16169 2956 // https://issues.dlang.org/show_bug.cgi?id=17966 2957 // https://issues.dlang.org/show_bug.cgi?id=19532 2958 // Using multiwayMerge/chunkBy 2959 { 2960 import std.algorithm.comparison : equal; 2961 import std.algorithm.setops : multiwayMerge; 2962 2963 { 2964 auto a0 = [2, 3, 5]; 2965 auto a1 = [2, 4, 5]; 2966 auto a2 = [1, 2, 4, 5]; 2967 2968 auto expected = [[1], [2, 2, 2], [3], [4, 4], [5, 5, 5]]; 2969 auto r = multiwayMerge([a0, a1, a2]).chunkBy!((a, b) => a == b); 2970 assert(r.equal!equal(expected)); 2971 } 2972 { 2973 auto a0 = [2, 3, 5]; 2974 auto a1 = [2, 4, 5]; 2975 auto a2 = [1, 2, 4, 5]; 2976 2977 auto expected = [[1], [2, 2, 2], [3], [4, 4], [5, 5, 5]]; 2978 auto r = 2979 multiwayMerge([valInputRange(a0), valInputRange(a1), valInputRange(a2)]) 2980 .chunkBy!((a, b) => a == b); 2981 assert(r.equal!equal(expected)); 2982 } 2983 { 2984 auto a0 = [2, 3, 5]; 2985 auto a1 = [2, 4, 5]; 2986 auto a2 = [1, 2, 4, 5]; 2987 2988 auto expected = [[1], [2, 2, 2], [3], [4, 4], [5, 5, 5]]; 2989 auto r = 2990 multiwayMerge([refInputRange(a0), refInputRange(a1), refInputRange(a2)]) 2991 .chunkBy!((a, b) => a == b); 2992 assert(r.equal!equal(expected)); 2993 } 2994 } 2995 2996 // https://issues.dlang.org/show_bug.cgi?id=20496 2997 { 2998 auto r = [1,1,1,2,2,2,3,3,3]; 2999 r.chunkBy!((ref e1, ref e2) => e1 == e2); 3000 } 3001 } 3002 3003 3004 3005 // https://issues.dlang.org/show_bug.cgi?id=13805 3006 @safe unittest 3007 { 3008 [""].map!((s) => s).chunkBy!((x, y) => true); 3009 } 3010 3011 /** 3012 Splits a forward range into subranges in places determined by a binary 3013 predicate called with adjacent elements. 3014 3015 When iterating, one element of `r` is compared with `pred` to the next 3016 element. If `pred` returns true, a new subrange is started for the next element. 3017 Otherwise, they are part of the same subrange. 3018 3019 If the elements are compared with an inequality (!=) operator, consider 3020 $(LREF chunkBy) instead, as it's likely faster to execute. 3021 3022 Params: 3023 pred = Predicate for determining where to split. The earlier element in the 3024 source range is always given as the first argument. 3025 r = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to be split. 3026 3027 Returns: A range of subranges of `r`, split such that within a given subrange, 3028 calling `pred` with any pair of adjacent elements as arguments returns `false`. 3029 Copying the range currently has reference semantics, but this may change in the future. 3030 3031 See_also: 3032 $(LREF splitter), which uses elements as splitters instead of element-to-element 3033 relations. 3034 */ 3035 3036 auto splitWhen(alias pred, Range)(Range r) 3037 if (is(typeof(binaryFun!pred(r.front, r.front)) : bool) && isForwardRange!Range) 3038 { import std.functional : not; 3039 return ChunkByImpl!(not!pred, not!pred, GroupingOpType.binaryAny, Range)(r); 3040 } 3041 3042 /// 3043 nothrow pure @safe unittest 3044 { 3045 import std.algorithm.comparison : equal; 3046 import std.range : dropExactly; 3047 auto source = [4, 3, 2, 11, 0, -3, -3, 5, 3, 0]; 3048 3049 auto result1 = source.splitWhen!((a,b) => a <= b); 3050 assert(result1.save.equal!equal([ 3051 [4, 3, 2], 3052 [11, 0, -3], 3053 [-3], 3054 [5, 3, 0] 3055 ])); 3056 3057 //splitWhen, like chunkBy, is currently a reference range (this may change 3058 //in future). Remember to call `save` when appropriate. 3059 auto result2 = result1.dropExactly(2); 3060 assert(result1.save.equal!equal([ 3061 [-3], 3062 [5, 3, 0] 3063 ])); 3064 } 3065 3066 //ensure we don't iterate the underlying range twice 3067 nothrow @safe unittest 3068 { 3069 import std.algorithm.comparison : equal; 3070 import std.math.algebraic : abs; 3071 3072 struct SomeRange 3073 { 3074 int[] elements; 3075 static int popfrontsSoFar; 3076 3077 auto front(){return elements[0];} 3078 nothrow @safe void popFront() 3079 { popfrontsSoFar++; 3080 elements = elements[1 .. $]; 3081 } 3082 auto empty(){return elements.length == 0;} 3083 auto save(){return this;} 3084 } 3085 3086 auto result = SomeRange([10, 9, 8, 5, 0, 1, 0, 8, 11, 10, 8, 12]) 3087 .splitWhen!((a, b) => abs(a - b) >= 3); 3088 3089 assert(result.equal!equal([ 3090 [10, 9, 8], 3091 [5], 3092 [0, 1, 0], 3093 [8], 3094 [11, 10, 8], 3095 [12] 3096 ])); 3097 3098 assert(SomeRange.popfrontsSoFar == 12); 3099 } 3100 3101 // Issue 13595 3102 @safe unittest 3103 { 3104 import std.algorithm.comparison : equal; 3105 auto r = [1, 2, 3, 4, 5, 6, 7, 8, 9].splitWhen!((x, y) => ((x*y) % 3) > 0); 3106 assert(r.equal!equal([ 3107 [1], 3108 [2, 3, 4], 3109 [5, 6, 7], 3110 [8, 9] 3111 ])); 3112 } 3113 3114 nothrow pure @safe unittest 3115 { 3116 // Grouping by maximum adjacent difference: 3117 import std.math.algebraic : abs; 3118 import std.algorithm.comparison : equal; 3119 auto r3 = [1, 3, 2, 5, 4, 9, 10].splitWhen!((a, b) => abs(a-b) >= 3); 3120 assert(r3.equal!equal([ 3121 [1, 3, 2], 3122 [5, 4], 3123 [9, 10] 3124 ])); 3125 } 3126 3127 // empty range splitWhen 3128 @nogc nothrow pure @system unittest 3129 { 3130 int[1] sliceable; 3131 auto result = sliceable[0 .. 0].splitWhen!((a,b) => a+b > 10); 3132 assert(result.empty); 3133 } 3134 3135 // joiner 3136 /** 3137 Lazily joins a range of ranges with a separator. The separator itself 3138 is a range. If a separator is not provided, then the ranges are 3139 joined directly without anything in between them (often called `flatten` 3140 in other languages). 3141 3142 Params: 3143 r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of input 3144 ranges to be joined. 3145 sep = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) of 3146 element(s) to serve as separators in the joined range. 3147 3148 Returns: 3149 A range of elements in the joined range. This will be a bidirectional range if 3150 both outer and inner ranges of `RoR` are at least bidirectional ranges. Else if 3151 both outer and inner ranges of `RoR` are forward ranges, the returned range will 3152 be likewise. Otherwise it will be only an input range. The 3153 $(REF_ALTTEXT range bidirectionality, isBidirectionalRange, std,range,primitives) 3154 is propagated if no separator is specified. 3155 3156 See_also: 3157 $(REF chain, std,range), which chains a sequence of ranges with compatible elements 3158 into a single range. 3159 3160 Note: 3161 When both outer and inner ranges of `RoR` are bidirectional and the joiner is 3162 iterated from the back to the front, the separator will still be consumed from 3163 front to back, even if it is a bidirectional range too. 3164 */ 3165 auto joiner(RoR, Separator)(RoR r, Separator sep) 3166 { 3167 static assert(isInputRange!RoR, "The type of RoR '", RoR.stringof 3168 , " must be an InputRange (isInputRange!", RoR.stringof, ")."); 3169 static assert(isInputRange!(ElementType!RoR), "The ElementyType of RoR '" 3170 , ElementType!(RoR).stringof, "' must be an InputRange " 3171 , "(isInputRange!(ElementType!(", RoR.stringof , ")))."); 3172 static assert(isForwardRange!Separator, "The type of the Separator '" 3173 , Separator.stringof, "' must be a ForwardRange (isForwardRange!(" 3174 , Separator.stringof, "))."); 3175 static assert(is(ElementType!Separator : ElementType!(ElementType!RoR)) 3176 , "The type of the elements of the separator range does not match " 3177 , "the type of the elements that are joined. Separator type '" 3178 , ElementType!(Separator).stringof, "' is not implicitly" 3179 , "convertible to range element type '" 3180 , ElementType!(ElementType!RoR).stringof, "' (is(ElementType!" 3181 , Separator.stringof, " : ElementType!(ElementType!", RoR.stringof 3182 , ")))."); 3183 3184 static struct Result 3185 { 3186 private RoR _items; 3187 private ElementType!RoR _current; 3188 bool inputStartsWithEmpty = false; 3189 static if (isBidirectional) 3190 { 3191 private ElementType!RoR _currentBack; 3192 bool inputEndsWithEmpty = false; 3193 } 3194 enum isBidirectional = isBidirectionalRange!RoR && 3195 isBidirectionalRange!(ElementType!RoR); 3196 static if (isRandomAccessRange!Separator) 3197 { 3198 static struct CurrentSep 3199 { 3200 private Separator _sep; 3201 private size_t sepIndex; 3202 private size_t sepLength; // cache the length for performance 3203 auto front() { return _sep[sepIndex]; } 3204 void popFront() { sepIndex++; } 3205 auto empty() { return sepIndex >= sepLength; } 3206 auto save() 3207 { 3208 auto copy = this; 3209 copy._sep = _sep; 3210 return copy; 3211 } 3212 void reset() 3213 { 3214 sepIndex = 0; 3215 } 3216 3217 void initialize(Separator sep) 3218 { 3219 _sep = sep; 3220 sepIndex = sepLength = _sep.length; 3221 } 3222 } 3223 } 3224 else 3225 { 3226 static struct CurrentSep 3227 { 3228 private Separator _sep; 3229 Separator payload; 3230 3231 alias payload this; 3232 3233 auto save() 3234 { 3235 auto copy = this; 3236 copy._sep = _sep; 3237 return copy; 3238 } 3239 3240 void reset() 3241 { 3242 payload = _sep.save; 3243 } 3244 3245 void initialize(Separator sep) 3246 { 3247 _sep = sep; 3248 } 3249 } 3250 } 3251 3252 private CurrentSep _currentSep; 3253 static if (isBidirectional) 3254 { 3255 private CurrentSep _currentBackSep; 3256 } 3257 3258 private void setItem() 3259 { 3260 if (!_items.empty) 3261 { 3262 // If we're exporting .save, we must not consume any of the 3263 // subranges, since RoR.save does not guarantee that the states 3264 // of the subranges are also saved. 3265 static if (isForwardRange!RoR && 3266 isForwardRange!(ElementType!RoR)) 3267 _current = _items.front.save; 3268 else 3269 _current = _items.front; 3270 } 3271 } 3272 3273 private void useSeparator() 3274 { 3275 // Separator must always come after an item. 3276 assert(_currentSep.empty, 3277 "Attempting to reset a non-empty separator"); 3278 assert(!_items.empty, 3279 "Attempting to use a separator in an empty joiner"); 3280 _items.popFront(); 3281 3282 // If there are no more items, we're done, since separators are not 3283 // terminators. 3284 if (_items.empty) return; 3285 3286 if (_currentSep._sep.empty) 3287 { 3288 // Advance to the next range in the 3289 // input 3290 while (_items.front.empty) 3291 { 3292 _items.popFront(); 3293 if (_items.empty) return; 3294 } 3295 setItem; 3296 } 3297 else 3298 { 3299 _currentSep.reset; 3300 assert(!_currentSep.empty, "separator must not be empty"); 3301 } 3302 } 3303 3304 this(RoR items, Separator sep) 3305 { 3306 _items = items; 3307 _currentSep.initialize(sep); 3308 static if (isBidirectional) 3309 _currentBackSep.initialize(sep); 3310 3311 //mixin(useItem); // _current should be initialized in place 3312 if (_items.empty) 3313 { 3314 _current = _current.init; // set invalid state 3315 static if (isBidirectional) 3316 _currentBack = _currentBack.init; 3317 } 3318 else 3319 { 3320 // If we're exporting .save, we must not consume any of the 3321 // subranges, since RoR.save does not guarantee that the states 3322 // of the subranges are also saved. 3323 static if (isForwardRange!RoR && 3324 isForwardRange!(ElementType!RoR)) 3325 _current = _items.front.save; 3326 else 3327 _current = _items.front; 3328 3329 static if (isBidirectional) 3330 { 3331 _currentBack = _items.back.save; 3332 3333 if (_currentBack.empty) 3334 { 3335 // No data in the currentBack item - toggle to use 3336 // the separator 3337 inputEndsWithEmpty = true; 3338 } 3339 } 3340 3341 if (_current.empty) 3342 { 3343 // No data in the current item - toggle to use the separator 3344 inputStartsWithEmpty = true; 3345 3346 // If RoR contains a single empty element, 3347 // the returned Result will always be empty 3348 import std.range : dropOne; 3349 static if (hasLength!RoR) 3350 { 3351 if (_items.length == 1) 3352 _items.popFront; 3353 } 3354 else static if (isForwardRange!RoR) 3355 { 3356 if (_items.save.dropOne.empty) 3357 _items.popFront; 3358 } 3359 else 3360 { 3361 auto _itemsCopy = _items; 3362 if (_itemsCopy.dropOne.empty) 3363 _items.popFront; 3364 } 3365 } 3366 } 3367 } 3368 3369 @property auto empty() 3370 { 3371 return _items.empty; 3372 } 3373 3374 //no data in the first item of the initial range - use the separator 3375 private enum useSepIfFrontIsEmpty = q{ 3376 if (inputStartsWithEmpty) 3377 { 3378 useSeparator(); 3379 inputStartsWithEmpty = false; 3380 } 3381 }; 3382 3383 @property ElementType!(ElementType!RoR) front() 3384 { 3385 mixin(useSepIfFrontIsEmpty); 3386 if (!_currentSep.empty) return _currentSep.front; 3387 assert(!_current.empty, "Attempting to fetch the front of an empty joiner."); 3388 return _current.front; 3389 } 3390 3391 void popFront() 3392 { 3393 assert(!_items.empty, "Attempting to popFront an empty joiner."); 3394 // Using separator? 3395 mixin(useSepIfFrontIsEmpty); 3396 3397 if (!_currentSep.empty) 3398 { 3399 _currentSep.popFront(); 3400 if (_currentSep.empty && !_items.empty) 3401 { 3402 setItem; 3403 if (_current.empty) 3404 { 3405 // No data in the current item - toggle to use the separator 3406 useSeparator(); 3407 } 3408 } 3409 } 3410 else 3411 { 3412 // we're using the range 3413 _current.popFront(); 3414 if (_current.empty) 3415 useSeparator(); 3416 } 3417 } 3418 3419 static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR)) 3420 { 3421 @property auto save() 3422 { 3423 Result copy = this; 3424 copy._items = _items.save; 3425 copy._current = _current.save; 3426 copy._currentSep = _currentSep.save; 3427 static if (isBidirectional) 3428 { 3429 copy._currentBack = _currentBack; 3430 copy._currentBackSep = _currentBackSep; 3431 } 3432 return copy; 3433 } 3434 } 3435 3436 static if (isBidirectional) 3437 { 3438 //no data in the last item of the initial range - use the separator 3439 private enum useSepIfBackIsEmpty = q{ 3440 if (inputEndsWithEmpty) 3441 { 3442 useBackSeparator; 3443 inputEndsWithEmpty = false; 3444 } 3445 }; 3446 3447 private void setBackItem() 3448 { 3449 if (!_items.empty) 3450 { 3451 _currentBack = _items.back.save; 3452 } 3453 } 3454 3455 private void useBackSeparator() 3456 { 3457 // Separator must always come after an item. 3458 assert(_currentBackSep.empty, 3459 "Attempting to reset a non-empty separator"); 3460 assert(!_items.empty, 3461 "Attempting to use a separator in an empty joiner"); 3462 _items.popBack(); 3463 3464 // If there are no more items, we're done, since separators are not 3465 // terminators. 3466 if (_items.empty) return; 3467 3468 if (_currentBackSep._sep.empty) 3469 { 3470 // Advance to the next range in the 3471 // input 3472 while (_items.back.empty) 3473 { 3474 _items.popBack(); 3475 if (_items.empty) return; 3476 } 3477 setBackItem; 3478 } 3479 else 3480 { 3481 _currentBackSep.reset; 3482 assert(!_currentBackSep.empty, "separator must not be empty"); 3483 } 3484 } 3485 3486 @property ElementType!(ElementType!RoR) back() 3487 { 3488 mixin(useSepIfBackIsEmpty); 3489 3490 if (!_currentBackSep.empty) return _currentBackSep.front; 3491 assert(!_currentBack.empty, "Attempting to fetch the back of an empty joiner."); 3492 return _currentBack.back; 3493 } 3494 3495 void popBack() 3496 { 3497 assert(!_items.empty, "Attempting to popBack an empty joiner."); 3498 3499 mixin(useSepIfBackIsEmpty); 3500 3501 if (!_currentBackSep.empty) 3502 { 3503 _currentBackSep.popFront(); 3504 if (_currentBackSep.empty && !_items.empty) 3505 { 3506 setBackItem; 3507 if (_currentBack.empty) 3508 { 3509 // No data in the current item - toggle to use the separator 3510 useBackSeparator(); 3511 } 3512 } 3513 } 3514 else 3515 { 3516 // we're using the range 3517 _currentBack.popBack(); 3518 if (_currentBack.empty) 3519 useBackSeparator(); 3520 } 3521 } 3522 } 3523 } 3524 return Result(r, sep); 3525 } 3526 3527 /// 3528 @safe unittest 3529 { 3530 import std.algorithm.comparison : equal; 3531 import std.conv : text; 3532 3533 assert(["abc", "def"].joiner.equal("abcdef")); 3534 assert(["Mary", "has", "a", "little", "lamb"] 3535 .joiner("...") 3536 .equal("Mary...has...a...little...lamb")); 3537 assert(["", "abc"].joiner("xyz").equal("xyzabc")); 3538 assert([""].joiner("xyz").equal("")); 3539 assert(["", ""].joiner("xyz").equal("xyz")); 3540 } 3541 3542 @safe pure nothrow unittest 3543 { 3544 //joiner with separator can return a bidirectional range 3545 assert(isBidirectionalRange!(typeof(["abc", "def"].joiner("...")))); 3546 } 3547 3548 @system unittest 3549 { 3550 import std.algorithm.comparison : equal; 3551 import std.range.interfaces; 3552 import std.range.primitives; 3553 // joiner() should work for non-forward ranges too. 3554 auto r = inputRangeObject(["abc", "def"]); 3555 assert(equal(joiner(r, "xyz"), "abcxyzdef")); 3556 } 3557 3558 @system unittest 3559 { 3560 import std.algorithm.comparison : equal; 3561 import std.range; 3562 3563 // Related to https://issues.dlang.org/show_bug.cgi?id=8061 3564 auto r = joiner([ 3565 inputRangeObject("abc"), 3566 inputRangeObject("def"), 3567 ], "-*-"); 3568 3569 assert(equal(r, "abc-*-def")); 3570 3571 // Test case where separator is specified but is empty. 3572 auto s = joiner([ 3573 inputRangeObject("abc"), 3574 inputRangeObject("def"), 3575 ], ""); 3576 3577 assert(equal(s, "abcdef")); 3578 3579 // Test empty separator with some empty elements 3580 auto t = joiner([ 3581 inputRangeObject("abc"), 3582 inputRangeObject(""), 3583 inputRangeObject("def"), 3584 inputRangeObject(""), 3585 ], ""); 3586 3587 assert(equal(t, "abcdef")); 3588 3589 // Test empty elements with non-empty separator 3590 auto u = joiner([ 3591 inputRangeObject(""), 3592 inputRangeObject("abc"), 3593 inputRangeObject(""), 3594 inputRangeObject("def"), 3595 inputRangeObject(""), 3596 ], "+-"); 3597 3598 assert(equal(u, "+-abc+-+-def+-")); 3599 3600 // https://issues.dlang.org/show_bug.cgi?id=13441: only(x) as separator 3601 string[][] lines = [null]; 3602 lines 3603 .joiner(only("b")) 3604 .array(); 3605 } 3606 3607 @safe unittest 3608 { 3609 import std.algorithm.comparison : equal; 3610 3611 // Transience correctness test 3612 struct TransientRange 3613 { 3614 @safe: 3615 int[][] src; 3616 int[] buf; 3617 3618 this(int[][] _src) 3619 { 3620 src = _src; 3621 buf.length = 100; 3622 } 3623 @property bool empty() { return src.empty; } 3624 @property int[] front() 3625 { 3626 assert(src.front.length <= buf.length); 3627 buf[0 .. src.front.length] = src.front[0..$]; 3628 return buf[0 .. src.front.length]; 3629 } 3630 void popFront() { src.popFront(); } 3631 } 3632 3633 // Test embedded empty elements 3634 auto tr1 = TransientRange([[], [1,2,3], [], [4]]); 3635 assert(equal(joiner(tr1, [0]), [0,1,2,3,0,0,4])); 3636 3637 // Test trailing empty elements 3638 auto tr2 = TransientRange([[], [1,2,3], []]); 3639 assert(equal(joiner(tr2, [0]), [0,1,2,3,0])); 3640 3641 // Test no empty elements 3642 auto tr3 = TransientRange([[1,2], [3,4]]); 3643 assert(equal(joiner(tr3, [0,1]), [1,2,0,1,3,4])); 3644 3645 // Test consecutive empty elements 3646 auto tr4 = TransientRange([[1,2], [], [], [], [3,4]]); 3647 assert(equal(joiner(tr4, [0,1]), [1,2,0,1,0,1,0,1,0,1,3,4])); 3648 3649 // Test consecutive trailing empty elements 3650 auto tr5 = TransientRange([[1,2], [3,4], [], []]); 3651 assert(equal(joiner(tr5, [0,1]), [1,2,0,1,3,4,0,1,0,1])); 3652 } 3653 3654 @safe unittest 3655 { 3656 static assert(isInputRange!(typeof(joiner([""], "")))); 3657 static assert(isForwardRange!(typeof(joiner([""], "")))); 3658 } 3659 3660 @safe pure unittest 3661 { 3662 { 3663 import std.algorithm.comparison : equal; 3664 auto r = joiner(["abc", "def", "ghi"], "?!"); 3665 char[] res; 3666 while (!r.empty) 3667 { 3668 res ~= r.back; 3669 r.popBack; 3670 } 3671 assert(res.equal("ihg?!fed?!cba")); 3672 } 3673 3674 { 3675 wchar[] sep = ['Ș', 'Ț']; 3676 auto r = joiner(["","abc",""],sep); 3677 wchar[] resFront; 3678 wchar[] resBack; 3679 3680 auto rCopy = r.save; 3681 while (!r.empty) 3682 { 3683 resFront ~= r.front; 3684 r.popFront; 3685 } 3686 3687 while (!rCopy.empty) 3688 { 3689 resBack ~= rCopy.back; 3690 rCopy.popBack; 3691 } 3692 3693 import std.algorithm.comparison : equal; 3694 3695 assert(resFront.equal("ȘȚabcȘȚ")); 3696 assert(resBack.equal("ȘȚcbaȘȚ")); 3697 } 3698 3699 { 3700 import std.algorithm.comparison : equal; 3701 auto r = [""]; 3702 r.popBack; 3703 assert(r.joiner("AB").equal("")); 3704 } 3705 3706 { 3707 auto r = ["", "", "", "abc", ""].joiner("../"); 3708 auto rCopy = r.save; 3709 3710 char[] resFront; 3711 char[] resBack; 3712 3713 while (!r.empty) 3714 { 3715 resFront ~= r.front; 3716 r.popFront; 3717 } 3718 3719 while (!rCopy.empty) 3720 { 3721 resBack ~= rCopy.back; 3722 rCopy.popBack; 3723 } 3724 3725 import std.algorithm.comparison : equal; 3726 3727 assert(resFront.equal("../../../abc../")); 3728 assert(resBack.equal("../cba../../../")); 3729 } 3730 3731 { 3732 auto r = ["", "abc", ""].joiner("./"); 3733 auto rCopy = r.save; 3734 r.popBack; 3735 rCopy.popFront; 3736 3737 auto rRev = r.save; 3738 auto rCopyRev = rCopy.save; 3739 3740 char[] r1, r2, r3, r4; 3741 3742 while (!r.empty) 3743 { 3744 r1 ~= r.back; 3745 r.popBack; 3746 } 3747 3748 while (!rCopy.empty) 3749 { 3750 r2 ~= rCopy.front; 3751 rCopy.popFront; 3752 } 3753 3754 while (!rRev.empty) 3755 { 3756 r3 ~= rRev.front; 3757 rRev.popFront; 3758 } 3759 3760 while (!rCopyRev.empty) 3761 { 3762 r4 ~= rCopyRev.back; 3763 rCopyRev.popBack; 3764 } 3765 3766 import std.algorithm.comparison : equal; 3767 3768 assert(r1.equal("/cba./")); 3769 assert(r2.equal("/abc./")); 3770 assert(r3.equal("./abc")); 3771 assert(r4.equal("./cba")); 3772 } 3773 } 3774 3775 @system unittest 3776 { 3777 import std.range; 3778 import std.algorithm.comparison : equal; 3779 3780 assert(inputRangeObject([""]).joiner("lz").equal("")); 3781 } 3782 3783 @safe pure unittest 3784 { 3785 struct inputRangeStrings 3786 { 3787 private string[] strings; 3788 3789 string front() 3790 { 3791 return strings[0]; 3792 } 3793 3794 void popFront() 3795 { 3796 strings = strings[1..$]; 3797 } 3798 3799 bool empty() const 3800 { 3801 return strings.length == 0; 3802 } 3803 } 3804 3805 auto arr = inputRangeStrings([""]); 3806 3807 import std.algorithm.comparison : equal; 3808 3809 assert(arr.joiner("./").equal("")); 3810 } 3811 3812 @safe pure unittest 3813 { 3814 auto r = joiner(["", "", "abc", "", ""], ""); 3815 char[] res; 3816 while (!r.empty) 3817 { 3818 res ~= r.back; 3819 r.popBack; 3820 } 3821 3822 import std.algorithm.comparison : equal; 3823 3824 assert(res.equal("cba")); 3825 } 3826 3827 /// Ditto 3828 auto joiner(RoR)(RoR r) 3829 if (isInputRange!RoR && isInputRange!(Unqual!(ElementType!RoR))) 3830 { 3831 static struct Result 3832 { 3833 private: 3834 RoR _items; 3835 Unqual!(ElementType!RoR) _current; 3836 enum isBidirectional = isForwardRange!RoR && isForwardRange!(ElementType!RoR) && 3837 isBidirectionalRange!RoR && isBidirectionalRange!(ElementType!RoR); 3838 static if (isBidirectional) 3839 { 3840 Unqual!(ElementType!RoR) _currentBack; 3841 bool reachedFinalElement; 3842 } 3843 3844 this(RoR items, ElementType!RoR current) 3845 { 3846 _items = items; 3847 _current = current; 3848 static if (isBidirectional && hasNested!Result) 3849 _currentBack = typeof(_currentBack).init; 3850 } 3851 3852 void replaceCurrent(typeof(_current) current) @trusted 3853 { 3854 import core.lifetime : move; 3855 3856 current.move(_current); 3857 } 3858 3859 static if (isBidirectional) 3860 { 3861 void replaceCurrentBack(typeof(_currentBack) currentBack) @trusted 3862 { 3863 import core.lifetime : move; 3864 3865 currentBack.move(_currentBack); 3866 } 3867 } 3868 3869 public: 3870 this(RoR r) 3871 { 3872 _items = r; 3873 // field _current must be initialized in constructor, because it is nested struct 3874 _current = typeof(_current).init; 3875 3876 static if (isBidirectional && hasNested!Result) 3877 _currentBack = typeof(_currentBack).init; 3878 mixin(popFrontEmptyElements); 3879 static if (isBidirectional) 3880 mixin(popBackEmptyElements); 3881 } 3882 static if (isInfinite!RoR) 3883 { 3884 enum bool empty = false; 3885 } 3886 else 3887 { 3888 @property auto empty() 3889 { 3890 return _items.empty; 3891 } 3892 } 3893 @property auto ref front() 3894 { 3895 assert(!empty, "Attempting to fetch the front of an empty joiner."); 3896 return _current.front; 3897 } 3898 void popFront() 3899 { 3900 assert(!_current.empty, "Attempting to popFront an empty joiner."); 3901 _current.popFront(); 3902 if (_current.empty) 3903 { 3904 assert(!_items.empty, "Attempting to popFront an empty joiner."); 3905 _items.popFront(); 3906 mixin(popFrontEmptyElements); 3907 } 3908 } 3909 3910 private enum popFrontEmptyElements = q{ 3911 // Skip over empty subranges. 3912 while (!_items.empty && _items.front.empty) 3913 { 3914 _items.popFront(); 3915 } 3916 if (!_items.empty) 3917 { 3918 // We cannot export .save method unless we ensure subranges are not 3919 // consumed when a .save'd copy of ourselves is iterated over. So 3920 // we need to .save each subrange we traverse. 3921 static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR)) 3922 replaceCurrent(_items.front.save); 3923 else 3924 replaceCurrent(_items.front); 3925 } 3926 else 3927 { 3928 replaceCurrent(typeof(_current).init); 3929 } 3930 }; 3931 3932 static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR)) 3933 { 3934 @property auto save() 3935 { 3936 // the null check is important if it is a class range, since null.save will segfault; issue #22359 3937 // could not just compare x is y here without static if due to a compiler assertion failure 3938 static if (is(typeof(null) : typeof(_current))) 3939 auto r = Result(_items.save, _current is null ? null : _current.save); 3940 else 3941 auto r = Result(_items.save, _current.save); 3942 static if (isBidirectional) 3943 { 3944 static if (is(typeof(null) : typeof(_currentBack))) 3945 r.replaceCurrentBack(_currentBack is null ? null : _currentBack.save); 3946 else 3947 r.replaceCurrentBack(_currentBack.save); 3948 r.reachedFinalElement = reachedFinalElement; 3949 } 3950 return r; 3951 } 3952 } 3953 3954 static if (hasAssignableElements!(ElementType!RoR)) 3955 { 3956 @property void front(ElementType!(ElementType!RoR) element) 3957 { 3958 assert(!empty, "Attempting to assign to front of an empty joiner."); 3959 _current.front = element; 3960 } 3961 3962 @property void front(ref ElementType!(ElementType!RoR) element) 3963 { 3964 assert(!empty, "Attempting to assign to front of an empty joiner."); 3965 _current.front = element; 3966 } 3967 } 3968 3969 static if (isBidirectional) 3970 { 3971 bool checkFinalElement() 3972 { 3973 import std.range : dropOne; 3974 3975 if (reachedFinalElement) 3976 return true; 3977 3978 static if (hasLength!(typeof(_items))) 3979 { 3980 if (_items.length == 1) 3981 reachedFinalElement = true; 3982 } 3983 else 3984 { 3985 if (_items.save.dropOne.empty) 3986 reachedFinalElement = true; 3987 } 3988 3989 return false; 3990 } 3991 3992 @property auto ref back() 3993 { 3994 assert(!empty, "Attempting to fetch the back of an empty joiner."); 3995 if (reachedFinalElement) 3996 return _current.back; 3997 else 3998 return _currentBack.back; 3999 } 4000 4001 void popBack() 4002 { 4003 assert(!_current.empty, "Attempting to popBack an empty joiner."); 4004 if (checkFinalElement) 4005 _current.popBack(); 4006 else 4007 _currentBack.popBack(); 4008 4009 bool isEmpty = reachedFinalElement ? _current.empty : _currentBack.empty; 4010 if (isEmpty) 4011 { 4012 assert(!_items.empty, "Attempting to popBack an empty joiner."); 4013 _items.popBack(); 4014 mixin(popBackEmptyElements); 4015 } 4016 } 4017 4018 private enum popBackEmptyElements = q{ 4019 // Skip over empty subranges. 4020 while (!_items.empty && _items.back.empty) 4021 { 4022 _items.popBack(); 4023 } 4024 if (!_items.empty) 4025 { 4026 checkFinalElement; 4027 // We cannot export .save method unless we ensure subranges are not 4028 // consumed when a .save'd copy of ourselves is iterated over. So 4029 // we need to .save each subrange we traverse. 4030 static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR)) 4031 { 4032 if (reachedFinalElement) 4033 replaceCurrent(_items.back.save); 4034 else 4035 replaceCurrentBack(_items.back.save); 4036 } 4037 else 4038 { 4039 if (reachedFinalElement) 4040 replaceCurrent(_items.back); 4041 else 4042 replaceCurrentBack(_items.back); 4043 } 4044 } 4045 else 4046 { 4047 replaceCurrent(typeof(_current).init); 4048 replaceCurrentBack(typeof(_currentBack).init); 4049 } 4050 }; 4051 4052 static if (hasAssignableElements!(ElementType!RoR)) 4053 { 4054 @property void back(ElementType!(ElementType!RoR) element) 4055 { 4056 assert(!empty, "Attempting to assign to back of an empty joiner."); 4057 if (reachedFinalElement) 4058 _current.back = element; 4059 else 4060 _currentBack.back = element; 4061 } 4062 4063 @property void back(ref ElementType!(ElementType!RoR) element) 4064 { 4065 assert(!empty, "Attempting to assign to back of an empty joiner."); 4066 if (reachedFinalElement) 4067 _current.back = element; 4068 else 4069 _currentBack.back = element; 4070 } 4071 } 4072 } 4073 } 4074 return Result(r); 4075 } 4076 4077 /// 4078 @safe unittest 4079 { 4080 import std.algorithm.comparison : equal; 4081 import std.range : repeat; 4082 4083 assert([""].joiner.equal("")); 4084 assert(["", ""].joiner.equal("")); 4085 assert(["", "abc"].joiner.equal("abc")); 4086 assert(["abc", ""].joiner.equal("abc")); 4087 assert(["abc", "def"].joiner.equal("abcdef")); 4088 assert(["Mary", "has", "a", "little", "lamb"].joiner.equal("Maryhasalittlelamb")); 4089 assert("abc".repeat(3).joiner.equal("abcabcabc")); 4090 } 4091 4092 /// joiner allows in-place mutation! 4093 @safe unittest 4094 { 4095 import std.algorithm.comparison : equal; 4096 auto a = [ [1, 2, 3], [42, 43] ]; 4097 auto j = joiner(a); 4098 j.front = 44; 4099 assert(a == [ [44, 2, 3], [42, 43] ]); 4100 assert(equal(j, [44, 2, 3, 42, 43])); 4101 } 4102 4103 /// insert characters fully lazily into a string 4104 @safe pure unittest 4105 { 4106 import std.algorithm.comparison : equal; 4107 import std.range : chain, cycle, iota, only, retro, take, zip; 4108 import std.format : format; 4109 4110 static immutable number = "12345678"; 4111 static immutable delimiter = ","; 4112 auto formatted = number.retro 4113 .zip(3.iota.cycle.take(number.length)) 4114 .map!(z => chain(z[0].only, z[1] == 2 ? delimiter : null)) 4115 .joiner 4116 .retro; 4117 static immutable expected = "12,345,678"; 4118 assert(formatted.equal(expected)); 4119 } 4120 4121 @safe unittest 4122 { 4123 import std.range.interfaces : inputRangeObject; 4124 static assert(isInputRange!(typeof(joiner([""])))); 4125 static assert(isForwardRange!(typeof(joiner([""])))); 4126 } 4127 4128 @system unittest 4129 { 4130 // this test is system because the virtual interface call to save 4131 // is flexible and thus cannot be inferred safe automatically 4132 4133 // https://issues.dlang.org/show_bug.cgi?id=22359 4134 import std.range; 4135 ForwardRange!int bug(int[][] r) 4136 { 4137 import std.range : inputRangeObject; 4138 import std.algorithm.iteration : map, joiner; 4139 4140 auto range = inputRangeObject(r); 4141 4142 return range.map!(a =>inputRangeObject(a)).joiner.inputRangeObject; 4143 } 4144 auto f = bug([[]]); 4145 f.save(); // should not segfault 4146 } 4147 4148 @safe unittest 4149 { 4150 // Initial version of PR #6115 caused a compilation failure for 4151 // https://github.com/BlackEdder/ggplotd/blob/d4428c08db5ffdc05dfd29690bf7da9073ea1dc5/source/ggplotd/stat.d#L562-L583 4152 import std.range : zip; 4153 int[] xCoords = [1, 2, 3]; 4154 int[] yCoords = [4, 5, 6]; 4155 auto coords = zip(xCoords, xCoords[1..$]).map!( (xr) { 4156 return zip(yCoords, yCoords[1..$]).map!( (yr) { 4157 return [ 4158 [[xr[0], xr[0], xr[1]], 4159 [yr[0], yr[1], yr[1]]], 4160 [[xr[0], xr[1], xr[1]], 4161 [yr[0], yr[0], yr[1]]] 4162 ]; 4163 }).joiner; 4164 }).joiner; 4165 } 4166 4167 @system unittest 4168 { 4169 import std.algorithm.comparison : equal; 4170 import std.range.interfaces : inputRangeObject; 4171 import std.range : retro; 4172 4173 // https://issues.dlang.org/show_bug.cgi?id=8240 4174 assert(equal(joiner([inputRangeObject("")]), "")); 4175 assert(equal(joiner([inputRangeObject("")]).retro, "")); 4176 4177 // https://issues.dlang.org/show_bug.cgi?id=8792 4178 auto b = [[1], [2], [3]]; 4179 auto jb = joiner(b); 4180 auto js = jb.save; 4181 assert(equal(jb, js)); 4182 4183 auto js2 = jb.save; 4184 jb.popFront(); 4185 assert(!equal(jb, js)); 4186 assert(equal(js2, js)); 4187 js.popFront(); 4188 assert(equal(jb, js)); 4189 assert(!equal(js2, js)); 4190 } 4191 4192 // https://issues.dlang.org/show_bug.cgi?id=19213 4193 @system unittest 4194 { 4195 auto results = [[1,2], [3,4]].map!(q => q.chunkBy!"a").joiner; 4196 int i = 1; 4197 foreach (ref e; results) 4198 assert(e[0] == i++); 4199 } 4200 4201 /// joiner can be bidirectional 4202 @safe unittest 4203 { 4204 import std.algorithm.comparison : equal; 4205 import std.range : retro; 4206 4207 auto a = [[1, 2, 3], [4, 5]]; 4208 auto j = a.joiner; 4209 j.back = 44; 4210 assert(a == [[1, 2, 3], [4, 44]]); 4211 assert(equal(j.retro, [44, 4, 3, 2, 1])); 4212 } 4213 4214 // bidirectional joiner: test for filtering empty elements 4215 @safe unittest 4216 { 4217 import std.algorithm.comparison : equal; 4218 import std.range : retro; 4219 4220 alias El = (e) => new int(e); 4221 auto a = [null, [null, El(1), null, El(2), null, El(3), null], null, [null, El(4), null, El(5), null]]; 4222 auto j = a.joiner; 4223 4224 alias deref = a => a is null ? -1 : *a; 4225 auto expected = [-1, 5, -1, 4, -1, -1, 3, -1, 2, -1, 1, -1]; 4226 // works with .save. 4227 assert(j.save.retro.map!deref.equal(expected)); 4228 // and without .save 4229 assert(j.retro.map!deref.equal(expected)); 4230 assert(j.retro.map!deref.equal(expected)); 4231 } 4232 4233 // bidirectional joiner is @nogc 4234 @safe @nogc unittest 4235 { 4236 import std.algorithm.comparison : equal; 4237 import std.range : iota, only, retro; 4238 4239 auto a = only(iota(1, 4), iota(4, 6)); 4240 auto j = a.joiner; 4241 static immutable expected = [5 , 4, 3, 2, 1]; 4242 assert(equal(j.retro, expected)); 4243 } 4244 4245 // bidirectional joiner supports assignment to the back 4246 @safe unittest 4247 { 4248 import std.algorithm.comparison : equal; 4249 import std.range : popBackN; 4250 4251 auto a = [[1, 2, 3], [4, 5]]; 4252 auto j = a.joiner; 4253 j.back = 55; 4254 assert(a == [[1, 2, 3], [4, 55]]); 4255 j.popBackN(2); 4256 j.back = 33; 4257 assert(a == [[1, 2, 33], [4, 55]]); 4258 } 4259 4260 // bidirectional joiner works with auto-decoding 4261 @safe unittest 4262 { 4263 import std.algorithm.comparison : equal; 4264 import std.range : retro; 4265 4266 auto a = ["😀😐", "😠"]; 4267 auto j = a.joiner; 4268 assert(j.retro.equal("😠😐😀")); 4269 } 4270 4271 // test two-side iteration 4272 @safe unittest 4273 { 4274 import std.algorithm.comparison : equal; 4275 import std.range : popBackN; 4276 4277 auto arrs = [ 4278 [[1], [2], [3], [4], [5]], 4279 [[1], [2, 3, 4], [5]], 4280 [[1, 2, 3, 4, 5]], 4281 ]; 4282 foreach (arr; arrs) 4283 { 4284 auto a = arr.joiner; 4285 assert(a.front == 1); 4286 assert(a.back == 5); 4287 a.popFront; 4288 assert(a.front == 2); 4289 assert(a.back == 5); 4290 a.popBack; 4291 assert(a.front == 2); 4292 assert(a.back == 4); 4293 a.popFront; 4294 assert(a.front == 3); 4295 assert(a.back == 4); 4296 a.popBack; 4297 assert(a.front == 3); 4298 assert(a.back == 3); 4299 a.popBack; 4300 assert(a.empty); 4301 } 4302 } 4303 4304 @safe unittest 4305 { 4306 import std.algorithm.comparison : equal; 4307 4308 struct TransientRange 4309 { 4310 @safe: 4311 int[] _buf; 4312 int[][] _values; 4313 this(int[][] values) 4314 { 4315 _values = values; 4316 _buf = new int[128]; 4317 } 4318 @property bool empty() 4319 { 4320 return _values.length == 0; 4321 } 4322 @property auto front() 4323 { 4324 foreach (i; 0 .. _values.front.length) 4325 { 4326 _buf[i] = _values[0][i]; 4327 } 4328 return _buf[0 .. _values.front.length]; 4329 } 4330 void popFront() 4331 { 4332 _values = _values[1 .. $]; 4333 } 4334 } 4335 4336 auto rr = TransientRange([[1,2], [3,4,5], [], [6,7]]); 4337 4338 // Can't use array() or equal() directly because they fail with transient 4339 // .front. 4340 int[] result; 4341 foreach (c; rr.joiner()) 4342 { 4343 result ~= c; 4344 } 4345 4346 assert(equal(result, [1,2,3,4,5,6,7])); 4347 } 4348 4349 @safe unittest 4350 { 4351 import std.algorithm.comparison : equal; 4352 import std.algorithm.internal : algoFormat; 4353 4354 struct TransientRange 4355 { 4356 @safe: 4357 dchar[] _buf; 4358 dstring[] _values; 4359 this(dstring[] values) 4360 { 4361 _buf.length = 128; 4362 _values = values; 4363 } 4364 @property bool empty() 4365 { 4366 return _values.length == 0; 4367 } 4368 @property auto front() 4369 { 4370 foreach (i; 0 .. _values.front.length) 4371 { 4372 _buf[i] = _values[0][i]; 4373 } 4374 return _buf[0 .. _values.front.length]; 4375 } 4376 void popFront() 4377 { 4378 _values = _values[1 .. $]; 4379 } 4380 } 4381 4382 auto rr = TransientRange(["abc"d, "12"d, "def"d, "34"d]); 4383 4384 // Can't use array() or equal() directly because they fail with transient 4385 // .front. 4386 dchar[] result; 4387 foreach (c; rr.joiner()) 4388 { 4389 result ~= c; 4390 } 4391 4392 import std.conv : to; 4393 assert(equal(result, "abc12def34"d), 4394 //Convert to string for assert's message 4395 to!string("Unexpected result: '%s'"d.algoFormat(result))); 4396 } 4397 4398 // https://issues.dlang.org/show_bug.cgi?id=8061 4399 @system unittest 4400 { 4401 import std.conv : to; 4402 import std.range.interfaces; 4403 4404 auto r = joiner([inputRangeObject("ab"), inputRangeObject("cd")]); 4405 assert(isForwardRange!(typeof(r))); 4406 4407 auto str = to!string(r); 4408 assert(str == "abcd"); 4409 } 4410 4411 @safe unittest 4412 { 4413 import std.range : repeat; 4414 4415 class AssignableRange 4416 { 4417 @safe: 4418 int element; 4419 @property int front() 4420 { 4421 return element; 4422 } 4423 alias back = front; 4424 4425 enum empty = false; 4426 4427 auto save() 4428 { 4429 return this; 4430 } 4431 4432 void popFront() {} 4433 alias popBack = popFront; 4434 4435 @property void front(int newValue) 4436 { 4437 element = newValue; 4438 } 4439 alias back = front; 4440 } 4441 4442 static assert(isInputRange!AssignableRange); 4443 static assert(is(ElementType!AssignableRange == int)); 4444 static assert(hasAssignableElements!AssignableRange); 4445 static assert(!hasLvalueElements!AssignableRange); 4446 4447 auto range = new AssignableRange(); 4448 assert(range.element == 0); 4449 { 4450 auto joined = joiner(repeat(range)); 4451 joined.front = 5; 4452 assert(range.element == 5); 4453 assert(joined.front == 5); 4454 4455 joined.popFront; 4456 int byRef = 7; 4457 joined.front = byRef; 4458 assert(range.element == byRef); 4459 assert(joined.front == byRef); 4460 } 4461 { 4462 auto joined = joiner(repeat(range)); 4463 joined.back = 5; 4464 assert(range.element == 5); 4465 assert(joined.back == 5); 4466 4467 joined.popBack; 4468 int byRef = 7; 4469 joined.back = byRef; 4470 assert(range.element == byRef); 4471 assert(joined.back == byRef); 4472 } 4473 } 4474 4475 // https://issues.dlang.org/show_bug.cgi?id=19850 4476 @safe pure unittest 4477 { 4478 assert([[0]].joiner.save.back == 0); 4479 } 4480 4481 // https://issues.dlang.org/show_bug.cgi?id=22561 4482 @safe pure unittest 4483 { 4484 import std.range : only; 4485 4486 static immutable struct S { int[] array; } 4487 assert([only(S(null))].joiner.front == S(null)); 4488 } 4489 4490 // https://issues.dlang.org/show_bug.cgi?id=22785 4491 @safe unittest 4492 { 4493 4494 import std.algorithm.iteration : joiner, map; 4495 import std.array : array; 4496 4497 static immutable struct S 4498 { 4499 int value; 4500 } 4501 4502 static immutable struct T 4503 { 4504 S[] arr; 4505 } 4506 4507 auto range = [T([S(3)]), T([S(4), S(5)])]; 4508 4509 assert(range.map!"a.arr".joiner.array == [S(3), S(4), S(5)]); 4510 } 4511 4512 /++ 4513 Implements the homonym function (also known as `accumulate`, $(D 4514 compress), `inject`, or `foldl`) present in various programming 4515 languages of functional flavor. There is also $(LREF fold) which does 4516 the same thing but with the opposite parameter order. 4517 4518 * Use `seed` to initialize an internal `accumulator`. 4519 * For each element `e` in $(D range), evaluate `accumulator = fun(result, e)`. 4520 * Return $(D accumulator). 4521 4522 The one-argument version `reduce!(fun)(range)` 4523 works similarly, but it uses the first element of the range as the 4524 seed (the range must be non-empty, else this throws). 4525 4526 If range has only one element, the functions are never invoked and 4527 `result` and the seed is returned unchanged. 4528 4529 Returns: 4530 the accumulated result 4531 4532 Params: 4533 fun = one or more functions of the form `Acc function(Acc, ElemT)` 4534 4535 See_Also: 4536 $(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function)) 4537 4538 $(LREF fold) is functionally equivalent to $(LREF _reduce) with the argument 4539 order reversed, and without the need to use $(REF_ALTTEXT `tuple`,tuple,std,typecons) 4540 for multiple seeds. This makes it easier to use in UFCS chains. 4541 4542 $(LREF sum) is similar to `reduce!((a, b) => a + b)` that offers 4543 pairwise summing of floating point numbers. 4544 +/ 4545 template reduce(fun...) 4546 if (fun.length >= 1) 4547 { 4548 import std.meta : staticMap; 4549 4550 alias binfuns = staticMap!(binaryFun, fun); 4551 static if (fun.length > 1) 4552 import std.typecons : tuple, isTuple; 4553 4554 /++ 4555 No-seed version. The first element of `r` is used as the seed's value. 4556 4557 For each function `f` in `fun`, the corresponding 4558 seed type `S` is `Unqual!(typeof(f(e, e)))`, where `e` is an 4559 element of `r`: `ElementType!R` for ranges, 4560 and `ForeachType!R` otherwise. 4561 4562 Once S has been determined, then `S s = e;` and `s = f(s, e);` 4563 must both be legal. 4564 4565 Params: 4566 r = an iterable value as defined by `isIterable` 4567 4568 Returns: 4569 the final result of the accumulator applied to the iterable 4570 4571 Throws: `Exception` if `r` is empty 4572 +/ 4573 auto reduce(R)(R r) 4574 if (isIterable!R) 4575 { 4576 import std.exception : enforce; 4577 alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R); 4578 alias Args = staticMap!(ReduceSeedType!E, binfuns); 4579 4580 static if (isInputRange!R) 4581 { 4582 // no need to throw if range is statically known to be non-empty 4583 static if (!__traits(compiles, 4584 { 4585 static assert(r.length > 0); 4586 })) 4587 enforce(!r.empty, "Cannot reduce an empty input range w/o an explicit seed value."); 4588 4589 Args result = r.front; 4590 r.popFront(); 4591 return reduceImpl!false(r, result); 4592 } 4593 else 4594 { 4595 auto result = Args.init; 4596 return reduceImpl!true(r, result); 4597 } 4598 } 4599 4600 /++ 4601 Seed version. The seed should be a single value if `fun` is a 4602 single function. If `fun` is multiple functions, then `seed` 4603 should be a $(REF Tuple, std,typecons), with one field per function in `f`. 4604 4605 For convenience, if the seed is const, or has qualified fields, then 4606 `reduce` will operate on an unqualified copy. If this happens 4607 then the returned type will not perfectly match `S`. 4608 4609 Use `fold` instead of `reduce` to use the seed version in a UFCS chain. 4610 4611 Params: 4612 seed = the initial value of the accumulator 4613 r = an iterable value as defined by `isIterable` 4614 4615 Returns: 4616 the final result of the accumulator applied to the iterable 4617 +/ 4618 auto reduce(S, R)(S seed, R r) 4619 if (isIterable!R) 4620 { 4621 static if (fun.length == 1) 4622 return reducePreImpl(r, seed); 4623 else 4624 { 4625 import std.algorithm.internal : algoFormat; 4626 static assert(isTuple!S, algoFormat("Seed %s should be a Tuple", S.stringof)); 4627 return reducePreImpl(r, seed.expand); 4628 } 4629 } 4630 4631 private auto reducePreImpl(R, Args...)(R r, ref Args args) 4632 { 4633 alias Result = staticMap!(Unqual, Args); 4634 static if (is(Result == Args)) 4635 alias result = args; 4636 else 4637 Result result = args; 4638 return reduceImpl!false(r, result); 4639 } 4640 4641 private auto reduceImpl(bool mustInitialize, R, Args...)(R r, ref Args args) 4642 if (isIterable!R) 4643 { 4644 import std.algorithm.internal : algoFormat; 4645 static assert(Args.length == fun.length, 4646 algoFormat("Seed %s does not have the correct amount of fields (should be %s)", Args.stringof, fun.length)); 4647 alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R); 4648 4649 static if (mustInitialize) bool initialized = false; 4650 foreach (/+auto ref+/ E e; r) // https://issues.dlang.org/show_bug.cgi?id=4707 4651 { 4652 foreach (i, f; binfuns) 4653 { 4654 static assert(!is(typeof(f(args[i], e))) || is(typeof(args[i] = f(args[i], e))), 4655 algoFormat( 4656 "Incompatible function/seed/element: %s/%s/%s", 4657 fullyQualifiedName!f, 4658 Args[i].stringof, 4659 E.stringof 4660 ) 4661 ); 4662 } 4663 4664 static if (mustInitialize) if (initialized == false) 4665 { 4666 import core.internal.lifetime : emplaceRef; 4667 foreach (i, f; binfuns) 4668 emplaceRef!(Args[i])(args[i], e); 4669 initialized = true; 4670 continue; 4671 } 4672 4673 foreach (i, f; binfuns) 4674 args[i] = f(args[i], e); 4675 } 4676 static if (mustInitialize) 4677 // no need to throw if range is statically known to be non-empty 4678 static if (!__traits(compiles, 4679 { 4680 static assert(r.length > 0); 4681 })) 4682 { 4683 if (!initialized) 4684 throw new Exception("Cannot reduce an empty iterable w/o an explicit seed value."); 4685 } 4686 4687 static if (Args.length == 1) 4688 return args[0]; 4689 else 4690 return tuple(args); 4691 } 4692 } 4693 4694 /** 4695 Many aggregate range operations turn out to be solved with `reduce` 4696 quickly and easily. The example below illustrates `reduce`'s 4697 remarkable power and flexibility. 4698 */ 4699 @safe unittest 4700 { 4701 import std.algorithm.comparison : max, min; 4702 import std.math.operations : isClose; 4703 import std.range; 4704 4705 int[] arr = [ 1, 2, 3, 4, 5 ]; 4706 // Sum all elements 4707 auto sum = reduce!((a,b) => a + b)(0, arr); 4708 assert(sum == 15); 4709 4710 // Sum again, using a string predicate with "a" and "b" 4711 sum = reduce!"a + b"(0, arr); 4712 assert(sum == 15); 4713 4714 // Compute the maximum of all elements 4715 auto largest = reduce!(max)(arr); 4716 assert(largest == 5); 4717 4718 // Max again, but with Uniform Function Call Syntax (UFCS) 4719 largest = arr.reduce!(max); 4720 assert(largest == 5); 4721 4722 // Compute the number of odd elements 4723 auto odds = reduce!((a,b) => a + (b & 1))(0, arr); 4724 assert(odds == 3); 4725 4726 // Compute the sum of squares 4727 auto ssquares = reduce!((a,b) => a + b * b)(0, arr); 4728 assert(ssquares == 55); 4729 4730 // Chain multiple ranges into seed 4731 int[] a = [ 3, 4 ]; 4732 int[] b = [ 100 ]; 4733 auto r = reduce!("a + b")(chain(a, b)); 4734 assert(r == 107); 4735 4736 // Mixing convertible types is fair game, too 4737 double[] c = [ 2.5, 3.0 ]; 4738 auto r1 = reduce!("a + b")(chain(a, b, c)); 4739 assert(isClose(r1, 112.5)); 4740 4741 // To minimize nesting of parentheses, Uniform Function Call Syntax can be used 4742 auto r2 = chain(a, b, c).reduce!("a + b"); 4743 assert(isClose(r2, 112.5)); 4744 } 4745 4746 /** 4747 Sometimes it is very useful to compute multiple aggregates in one pass. 4748 One advantage is that the computation is faster because the looping overhead 4749 is shared. That's why `reduce` accepts multiple functions. 4750 If two or more functions are passed, `reduce` returns a 4751 $(REF Tuple, std,typecons) object with one member per passed-in function. 4752 The number of seeds must be correspondingly increased. 4753 */ 4754 @safe unittest 4755 { 4756 import std.algorithm.comparison : max, min; 4757 import std.math.operations : isClose; 4758 import std.math.algebraic : sqrt; 4759 import std.typecons : tuple, Tuple; 4760 4761 double[] a = [ 3.0, 4, 7, 11, 3, 2, 5 ]; 4762 // Compute minimum and maximum in one pass 4763 auto r = reduce!(min, max)(a); 4764 // The type of r is Tuple!(int, int) 4765 assert(isClose(r[0], 2)); // minimum 4766 assert(isClose(r[1], 11)); // maximum 4767 4768 // Compute sum and sum of squares in one pass 4769 r = reduce!("a + b", "a + b * b")(tuple(0.0, 0.0), a); 4770 assert(isClose(r[0], 35)); // sum 4771 assert(isClose(r[1], 233)); // sum of squares 4772 // Compute average and standard deviation from the above 4773 auto avg = r[0] / a.length; 4774 assert(avg == 5); 4775 auto stdev = sqrt(r[1] / a.length - avg * avg); 4776 assert(cast(int) stdev == 2); 4777 } 4778 4779 @safe unittest 4780 { 4781 import std.algorithm.comparison : max, min; 4782 import std.range : chain; 4783 import std.typecons : tuple, Tuple; 4784 4785 double[] a = [ 3, 4 ]; 4786 auto r = reduce!("a + b")(0.0, a); 4787 assert(r == 7); 4788 r = reduce!("a + b")(a); 4789 assert(r == 7); 4790 r = reduce!(min)(a); 4791 assert(r == 3); 4792 double[] b = [ 100 ]; 4793 auto r1 = reduce!("a + b")(chain(a, b)); 4794 assert(r1 == 107); 4795 4796 // two funs 4797 auto r2 = reduce!("a + b", "a - b")(tuple(0.0, 0.0), a); 4798 assert(r2[0] == 7 && r2[1] == -7); 4799 auto r3 = reduce!("a + b", "a - b")(a); 4800 assert(r3[0] == 7 && r3[1] == -1); 4801 4802 a = [ 1, 2, 3, 4, 5 ]; 4803 // Stringize with commas 4804 string rep = reduce!("a ~ `, ` ~ to!(string)(b)")("", a); 4805 assert(rep[2 .. $] == "1, 2, 3, 4, 5", "["~rep[2 .. $]~"]"); 4806 } 4807 4808 @safe unittest 4809 { 4810 import std.algorithm.comparison : max, min; 4811 import std.exception : assertThrown; 4812 import std.range : iota; 4813 import std.typecons : tuple, Tuple; 4814 4815 // Test the opApply case. 4816 static struct OpApply 4817 { 4818 bool actEmpty; 4819 4820 int opApply(scope int delegate(ref int) @safe dg) 4821 { 4822 int res; 4823 if (actEmpty) return res; 4824 4825 foreach (i; 0 .. 100) 4826 { 4827 res = dg(i); 4828 if (res) break; 4829 } 4830 return res; 4831 } 4832 } 4833 4834 OpApply oa; 4835 auto hundredSum = reduce!"a + b"(iota(100)); 4836 assert(reduce!"a + b"(5, oa) == hundredSum + 5); 4837 assert(reduce!"a + b"(oa) == hundredSum); 4838 assert(reduce!("a + b", max)(oa) == tuple(hundredSum, 99)); 4839 assert(reduce!("a + b", max)(tuple(5, 0), oa) == tuple(hundredSum + 5, 99)); 4840 4841 // Test for throwing on empty range plus no seed. 4842 assertThrown(reduce!"a + b"([1, 2][0 .. 0])); 4843 4844 oa.actEmpty = true; 4845 assertThrown(reduce!"a + b"(oa)); 4846 } 4847 4848 @safe unittest 4849 { 4850 const float a = 0.0; 4851 const float[] b = [ 1.2, 3, 3.3 ]; 4852 float[] c = [ 1.2, 3, 3.3 ]; 4853 auto r = reduce!"a + b"(a, b); 4854 r = reduce!"a + b"(a, c); 4855 assert(r == 7.5); 4856 } 4857 4858 @safe unittest 4859 { 4860 // https://issues.dlang.org/show_bug.cgi?id=10408 4861 // Two-function reduce of a const array. 4862 import std.algorithm.comparison : max, min; 4863 import std.typecons : tuple, Tuple; 4864 4865 const numbers = [10, 30, 20]; 4866 immutable m = reduce!(min)(numbers); 4867 assert(m == 10); 4868 immutable minmax = reduce!(min, max)(numbers); 4869 assert(minmax == tuple(10, 30)); 4870 } 4871 4872 @safe unittest 4873 { 4874 // https://issues.dlang.org/show_bug.cgi?id=10709 4875 import std.typecons : tuple, Tuple; 4876 4877 enum foo = "a + 0.5 * b"; 4878 auto r = [0, 1, 2, 3]; 4879 auto r1 = reduce!foo(r); 4880 auto r2 = reduce!(foo, foo)(r); 4881 assert(r1 == 3); 4882 assert(r2 == tuple(3, 3)); 4883 } 4884 4885 @safe unittest 4886 { 4887 static struct OpApply 4888 { 4889 int opApply(int delegate(ref int) @safe dg) 4890 { 4891 int[] a = [1, 2, 3]; 4892 4893 int res = 0; 4894 foreach (ref e; a) 4895 { 4896 res = dg(e); 4897 if (res) break; 4898 } 4899 return res; 4900 } 4901 } 4902 //test CTFE and functions with context 4903 int fun(int a, int b) @safe {return a + b + 1;} 4904 auto foo() 4905 { 4906 import std.algorithm.comparison : max; 4907 import std.typecons : tuple, Tuple; 4908 4909 auto a = reduce!(fun)([1, 2, 3]); 4910 auto b = reduce!(fun, fun)([1, 2, 3]); 4911 auto c = reduce!(fun)(0, [1, 2, 3]); 4912 auto d = reduce!(fun, fun)(tuple(0, 0), [1, 2, 3]); 4913 auto e = reduce!(fun)(0, OpApply()); 4914 auto f = reduce!(fun, fun)(tuple(0, 0), OpApply()); 4915 4916 return max(a, b.expand, c, d.expand, e, f.expand); 4917 } 4918 auto a = foo(); 4919 assert(a == 9); 4920 enum b = foo(); 4921 assert(b == 9); 4922 } 4923 4924 @safe unittest 4925 { 4926 import std.algorithm.comparison : max, min; 4927 import std.typecons : tuple, Tuple; 4928 4929 //http://forum.dlang.org/post/oghtttkopzjshsuflelk@forum.dlang.org 4930 //Seed is tuple of const. 4931 static auto minmaxElement(alias F = min, alias G = max, R)(in R range) 4932 @safe pure nothrow 4933 if (isInputRange!R) 4934 { 4935 return reduce!(F, G)(tuple(ElementType!R.max, 4936 ElementType!R.min), range); 4937 } 4938 assert(minmaxElement([1, 2, 3]) == tuple(1, 3)); 4939 } 4940 4941 // https://issues.dlang.org/show_bug.cgi?id=12569 4942 @safe unittest 4943 { 4944 import std.algorithm.comparison : max, min; 4945 import std.typecons : tuple; 4946 dchar c = 'a'; 4947 reduce!(min, max)(tuple(c, c), "hello"); // OK 4948 static assert(!is(typeof(reduce!(min, max)(tuple(c), "hello")))); 4949 static assert(!is(typeof(reduce!(min, max)(tuple(c, c, c), "hello")))); 4950 4951 4952 //"Seed dchar should be a Tuple" 4953 static assert(!is(typeof(reduce!(min, max)(c, "hello")))); 4954 //"Seed (dchar) does not have the correct amount of fields (should be 2)" 4955 static assert(!is(typeof(reduce!(min, max)(tuple(c), "hello")))); 4956 //"Seed (dchar, dchar, dchar) does not have the correct amount of fields (should be 2)" 4957 static assert(!is(typeof(reduce!(min, max)(tuple(c, c, c), "hello")))); 4958 //"Incompatible function/seed/element: all(alias pred = "a")/int/dchar" 4959 static assert(!is(typeof(reduce!all(1, "hello")))); 4960 static assert(!is(typeof(reduce!(all, all)(tuple(1, 1), "hello")))); 4961 } 4962 4963 // https://issues.dlang.org/show_bug.cgi?id=13304 4964 @safe unittest 4965 { 4966 int[] data; 4967 static assert(is(typeof(reduce!((a, b) => a + b)(data)))); 4968 assert(data.length == 0); 4969 } 4970 4971 // https://issues.dlang.org/show_bug.cgi?id=13880 4972 // reduce shouldn't throw if the length is statically known 4973 pure nothrow @safe @nogc unittest 4974 { 4975 import std.algorithm.comparison : min; 4976 int[5] arr; 4977 arr[2] = -1; 4978 assert(arr.reduce!min == -1); 4979 4980 int[0] arr0; 4981 assert(reduce!min(42, arr0) == 42); 4982 } 4983 4984 //Helper for Reduce 4985 private template ReduceSeedType(E) 4986 { 4987 static template ReduceSeedType(alias fun) 4988 { 4989 import std.algorithm.internal : algoFormat; 4990 4991 alias ReduceSeedType = Unqual!(typeof(fun(lvalueOf!E, lvalueOf!E))); 4992 4993 //Check the Seed type is useable. 4994 ReduceSeedType s = ReduceSeedType.init; 4995 static assert(is(typeof({ReduceSeedType s = lvalueOf!E;})) && 4996 is(typeof(lvalueOf!ReduceSeedType = fun(lvalueOf!ReduceSeedType, lvalueOf!E))), 4997 algoFormat( 4998 "Unable to deduce an acceptable seed type for %s with element type %s.", 4999 fullyQualifiedName!fun, 5000 E.stringof 5001 ) 5002 ); 5003 } 5004 } 5005 5006 5007 /++ 5008 Implements the homonym function (also known as `accumulate`, $(D 5009 compress), `inject`, or `foldl`) present in various programming 5010 languages of functional flavor, iteratively calling one or more predicates. 5011 5012 $(P Each predicate in `fun` must take two arguments:) 5013 * An accumulator value 5014 * An element of the range `r` 5015 $(P Each function must return a value which implicitly converts to the 5016 type of the accumulator.) 5017 5018 $(P For a single function, 5019 the call `fold!(fun)(range, seed)` will:) 5020 5021 * Use `seed` to initialize an internal `accumulator`. 5022 * For each element `e` in $(D range), evaluate `accumulator = fun(result, e)`. 5023 * Return $(D accumulator). 5024 5025 $(P The one-argument version `fold!(fun)(range)` 5026 works similarly, but it uses the first element of the range as the 5027 seed (the range must be non-empty) and iterates over the remaining 5028 elements.) 5029 5030 Multiple results are produced when using multiple functions. 5031 5032 If range has only one element, the functions are never invoked and 5033 `result` and the seed is returned unchanged. 5034 5035 Params: 5036 fun = one or more functions of the form `Acc function(Acc, ElemT)` 5037 5038 See_Also: 5039 * $(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function)) 5040 5041 * $(LREF sum) is similar to `fold!((a, b) => a + b)` that offers 5042 precise summing of floating point numbers. 5043 5044 * `fold` is functionally equivalent to $(LREF reduce) with the argument order 5045 reversed, and without the need to use $(REF_ALTTEXT `tuple`,tuple,std,typecons) 5046 for multiple seeds. 5047 +/ 5048 template fold(fun...) 5049 if (fun.length >= 1) 5050 { 5051 /** 5052 Params: 5053 r = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to fold 5054 seeds = the initial values of each accumulator (optional), one for each predicate 5055 Returns: 5056 Either the accumulated result for a single predicate, or a 5057 $(REF_ALTTEXT `Tuple`,Tuple,std,typecons) of results. 5058 */ 5059 auto fold(R, S...)(R r, S seeds) 5060 { 5061 static if (S.length < 2) 5062 { 5063 return reduce!fun(seeds, r); 5064 } 5065 else 5066 { 5067 import std.typecons : tuple; 5068 return reduce!fun(tuple(seeds), r); 5069 } 5070 } 5071 } 5072 5073 /// 5074 @safe pure unittest 5075 { 5076 immutable arr = [1, 2, 3, 4, 5]; 5077 5078 // Sum all elements 5079 assert(arr.fold!((a, e) => a + e) == 15); 5080 5081 // Sum all elements with explicit seed 5082 assert(arr.fold!((a, e) => a + e)(6) == 21); 5083 5084 import std.algorithm.comparison : min, max; 5085 import std.typecons : tuple; 5086 5087 // Compute minimum and maximum at the same time 5088 assert(arr.fold!(min, max) == tuple(1, 5)); 5089 5090 // Compute minimum and maximum at the same time with seeds 5091 assert(arr.fold!(min, max)(0, 7) == tuple(0, 7)); 5092 5093 // Can be used in a UFCS chain 5094 assert(arr.map!(a => a + 1).fold!((a, e) => a + e) == 20); 5095 5096 // Return the last element of any range 5097 assert(arr.fold!((a, e) => e) == 5); 5098 } 5099 5100 @safe @nogc pure nothrow unittest 5101 { 5102 int[1] arr; 5103 static assert(!is(typeof(arr.fold!()))); 5104 static assert(!is(typeof(arr.fold!(a => a)))); 5105 static assert(is(typeof(arr.fold!((a, b) => a)))); 5106 static assert(is(typeof(arr.fold!((a, b) => a)(1)))); 5107 assert(arr.length == 1); 5108 } 5109 5110 /++ 5111 Similar to `fold`, but returns a range containing the successive reduced values. 5112 The call `cumulativeFold!(fun)(range, seed)` first assigns `seed` to an 5113 internal variable `result`, also called the accumulator. 5114 The returned range contains the values `result = fun(result, x)` lazily 5115 evaluated for each element `x` in `range`. Finally, the last element has the 5116 same value as `fold!(fun)(seed, range)`. 5117 The one-argument version `cumulativeFold!(fun)(range)` works similarly, but 5118 it returns the first element unchanged and uses it as seed for the next 5119 elements. 5120 This function is also known as 5121 $(HTTP en.cppreference.com/w/cpp/algorithm/partial_sum, partial_sum), 5122 $(HTTP docs.python.org/3/library/itertools.html#itertools.accumulate, accumulate), 5123 $(HTTP hackage.haskell.org/package/base-4.8.2.0/docs/Prelude.html#v:scanl, scan), 5124 $(HTTP mathworld.wolfram.com/CumulativeSum.html, Cumulative Sum). 5125 5126 Params: 5127 fun = one or more functions of the form `Acc function(Acc, ElemT)` 5128 5129 Returns: 5130 The function returns a range containing the consecutive reduced values. If 5131 there is more than one `fun`, the element type will be $(REF Tuple, 5132 std,typecons) containing one element for each `fun`. 5133 5134 See_Also: 5135 $(HTTP en.wikipedia.org/wiki/Prefix_sum, Prefix Sum) 5136 5137 Note: 5138 5139 In functional programming languages this is typically called `scan`, `scanl`, 5140 `scanLeft` or `reductions`. 5141 +/ 5142 template cumulativeFold(fun...) 5143 if (fun.length >= 1) 5144 { 5145 import std.meta : staticMap; 5146 private alias binfuns = staticMap!(binaryFun, fun); 5147 5148 /++ 5149 No-seed version. The first element of `r` is used as the seed's value. 5150 For each function `f` in `fun`, the corresponding seed type `S` is 5151 `Unqual!(typeof(f(e, e)))`, where `e` is an element of `r`: 5152 `ElementType!R`. 5153 Once `S` has been determined, then `S s = e;` and `s = f(s, e);` must 5154 both be legal. 5155 5156 Params: 5157 range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 5158 Returns: 5159 a range containing the consecutive reduced values. 5160 +/ 5161 auto cumulativeFold(R)(R range) 5162 if (isInputRange!(Unqual!R)) 5163 { 5164 return cumulativeFoldImpl(range); 5165 } 5166 5167 /++ 5168 Seed version. The seed should be a single value if `fun` is a single 5169 function. If `fun` is multiple functions, then `seed` should be a 5170 $(REF Tuple, std,typecons), with one field per function in `f`. 5171 For convenience, if the seed is `const`, or has qualified fields, then 5172 `cumulativeFold` will operate on an unqualified copy. If this happens 5173 then the returned type will not perfectly match `S`. 5174 5175 Params: 5176 range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 5177 seed = the initial value of the accumulator 5178 Returns: 5179 a range containing the consecutive reduced values. 5180 +/ 5181 auto cumulativeFold(R, S)(R range, S seed) 5182 if (isInputRange!(Unqual!R)) 5183 { 5184 static if (fun.length == 1) 5185 return cumulativeFoldImpl(range, seed); 5186 else 5187 return cumulativeFoldImpl(range, seed.expand); 5188 } 5189 5190 private auto cumulativeFoldImpl(R, Args...)(R range, ref Args args) 5191 { 5192 import std.algorithm.internal : algoFormat; 5193 5194 static assert(Args.length == 0 || Args.length == fun.length, 5195 algoFormat("Seed %s does not have the correct amount of fields (should be %s)", 5196 Args.stringof, fun.length)); 5197 5198 static if (args.length) 5199 alias State = staticMap!(Unqual, Args); 5200 else 5201 alias State = staticMap!(ReduceSeedType!(ElementType!R), binfuns); 5202 5203 foreach (i, f; binfuns) 5204 { 5205 static assert(!__traits(compiles, f(args[i], e)) || __traits(compiles, 5206 { args[i] = f(args[i], e); }()), 5207 algoFormat("Incompatible function/seed/element: %s/%s/%s", 5208 fullyQualifiedName!f, Args[i].stringof, E.stringof)); 5209 } 5210 5211 static struct Result 5212 { 5213 private: 5214 R source; 5215 State state; 5216 5217 this(R range, ref Args args) 5218 { 5219 source = range; 5220 if (source.empty) 5221 return; 5222 5223 foreach (i, f; binfuns) 5224 { 5225 static if (args.length) 5226 state[i] = f(args[i], source.front); 5227 else 5228 state[i] = source.front; 5229 } 5230 } 5231 5232 public: 5233 @property bool empty() 5234 { 5235 return source.empty; 5236 } 5237 5238 @property auto front() 5239 { 5240 assert(!empty, "Attempting to fetch the front of an empty cumulativeFold."); 5241 static if (fun.length > 1) 5242 { 5243 import std.typecons : tuple; 5244 return tuple(state); 5245 } 5246 else 5247 { 5248 return state[0]; 5249 } 5250 } 5251 5252 void popFront() 5253 { 5254 assert(!empty, "Attempting to popFront an empty cumulativeFold."); 5255 source.popFront; 5256 5257 if (source.empty) 5258 return; 5259 5260 foreach (i, f; binfuns) 5261 state[i] = f(state[i], source.front); 5262 } 5263 5264 static if (isForwardRange!R) 5265 { 5266 @property auto save() 5267 { 5268 auto result = this; 5269 result.source = source.save; 5270 return result; 5271 } 5272 } 5273 5274 mixin ImplementLength!source; 5275 } 5276 5277 return Result(range, args); 5278 } 5279 } 5280 5281 /// 5282 @safe unittest 5283 { 5284 import std.algorithm.comparison : max, min; 5285 import std.array : array; 5286 import std.math.operations : isClose; 5287 import std.range : chain; 5288 5289 int[] arr = [1, 2, 3, 4, 5]; 5290 // Partial sum of all elements 5291 auto sum = cumulativeFold!((a, b) => a + b)(arr, 0); 5292 assert(sum.array == [1, 3, 6, 10, 15]); 5293 5294 // Partial sum again, using a string predicate with "a" and "b" 5295 auto sum2 = cumulativeFold!"a + b"(arr, 0); 5296 assert(sum2.array == [1, 3, 6, 10, 15]); 5297 5298 // Compute the partial maximum of all elements 5299 auto largest = cumulativeFold!max(arr); 5300 assert(largest.array == [1, 2, 3, 4, 5]); 5301 5302 // Partial max again, but with Uniform Function Call Syntax (UFCS) 5303 largest = arr.cumulativeFold!max; 5304 assert(largest.array == [1, 2, 3, 4, 5]); 5305 5306 // Partial count of odd elements 5307 auto odds = arr.cumulativeFold!((a, b) => a + (b & 1))(0); 5308 assert(odds.array == [1, 1, 2, 2, 3]); 5309 5310 // Compute the partial sum of squares 5311 auto ssquares = arr.cumulativeFold!((a, b) => a + b * b)(0); 5312 assert(ssquares.array == [1, 5, 14, 30, 55]); 5313 5314 // Chain multiple ranges into seed 5315 int[] a = [3, 4]; 5316 int[] b = [100]; 5317 auto r = cumulativeFold!"a + b"(chain(a, b)); 5318 assert(r.array == [3, 7, 107]); 5319 5320 // Mixing convertible types is fair game, too 5321 double[] c = [2.5, 3.0]; 5322 auto r1 = cumulativeFold!"a + b"(chain(a, b, c)); 5323 assert(isClose(r1, [3, 7, 107, 109.5, 112.5])); 5324 5325 // To minimize nesting of parentheses, Uniform Function Call Syntax can be used 5326 auto r2 = chain(a, b, c).cumulativeFold!"a + b"; 5327 assert(isClose(r2, [3, 7, 107, 109.5, 112.5])); 5328 } 5329 5330 /** 5331 Sometimes it is very useful to compute multiple aggregates in one pass. 5332 One advantage is that the computation is faster because the looping overhead 5333 is shared. That's why `cumulativeFold` accepts multiple functions. 5334 If two or more functions are passed, `cumulativeFold` returns a $(REF Tuple, 5335 std,typecons) object with one member per passed-in function. 5336 The number of seeds must be correspondingly increased. 5337 */ 5338 @safe unittest 5339 { 5340 import std.algorithm.comparison : max, min; 5341 import std.algorithm.iteration : map; 5342 import std.math.operations : isClose; 5343 import std.typecons : tuple; 5344 5345 double[] a = [3.0, 4, 7, 11, 3, 2, 5]; 5346 // Compute minimum and maximum in one pass 5347 auto r = a.cumulativeFold!(min, max); 5348 // The type of r is Tuple!(int, int) 5349 assert(isClose(r.map!"a[0]", [3, 3, 3, 3, 3, 2, 2])); // minimum 5350 assert(isClose(r.map!"a[1]", [3, 4, 7, 11, 11, 11, 11])); // maximum 5351 5352 // Compute sum and sum of squares in one pass 5353 auto r2 = a.cumulativeFold!("a + b", "a + b * b")(tuple(0.0, 0.0)); 5354 assert(isClose(r2.map!"a[0]", [3, 7, 14, 25, 28, 30, 35])); // sum 5355 assert(isClose(r2.map!"a[1]", [9, 25, 74, 195, 204, 208, 233])); // sum of squares 5356 } 5357 5358 @safe unittest 5359 { 5360 import std.algorithm.comparison : equal, max, min; 5361 import std.conv : to; 5362 import std.range : chain; 5363 import std.typecons : tuple; 5364 5365 double[] a = [3, 4]; 5366 auto r = a.cumulativeFold!("a + b")(0.0); 5367 assert(r.equal([3, 7])); 5368 auto r2 = cumulativeFold!("a + b")(a); 5369 assert(r2.equal([3, 7])); 5370 auto r3 = cumulativeFold!(min)(a); 5371 assert(r3.equal([3, 3])); 5372 double[] b = [100]; 5373 auto r4 = cumulativeFold!("a + b")(chain(a, b)); 5374 assert(r4.equal([3, 7, 107])); 5375 5376 // two funs 5377 auto r5 = cumulativeFold!("a + b", "a - b")(a, tuple(0.0, 0.0)); 5378 assert(r5.equal([tuple(3, -3), tuple(7, -7)])); 5379 auto r6 = cumulativeFold!("a + b", "a - b")(a); 5380 assert(r6.equal([tuple(3, 3), tuple(7, -1)])); 5381 5382 a = [1, 2, 3, 4, 5]; 5383 // Stringize with commas 5384 auto rep = cumulativeFold!("a ~ `, ` ~ to!string(b)")(a, ""); 5385 assert(rep.map!"a[2 .. $]".equal(["1", "1, 2", "1, 2, 3", "1, 2, 3, 4", "1, 2, 3, 4, 5"])); 5386 5387 // Test for empty range 5388 a = []; 5389 assert(a.cumulativeFold!"a + b".empty); 5390 assert(a.cumulativeFold!"a + b"(2.0).empty); 5391 } 5392 5393 @safe unittest 5394 { 5395 import std.algorithm.comparison : max, min; 5396 import std.array : array; 5397 import std.math.operations : isClose; 5398 import std.typecons : tuple; 5399 5400 const float a = 0.0; 5401 const float[] b = [1.2, 3, 3.3]; 5402 float[] c = [1.2, 3, 3.3]; 5403 5404 auto r = cumulativeFold!"a + b"(b, a); 5405 assert(isClose(r, [1.2, 4.2, 7.5])); 5406 5407 auto r2 = cumulativeFold!"a + b"(c, a); 5408 assert(isClose(r2, [1.2, 4.2, 7.5])); 5409 5410 const numbers = [10, 30, 20]; 5411 enum m = numbers.cumulativeFold!(min).array; 5412 assert(m == [10, 10, 10]); 5413 enum minmax = numbers.cumulativeFold!(min, max).array; 5414 assert(minmax == [tuple(10, 10), tuple(10, 30), tuple(10, 30)]); 5415 } 5416 5417 @safe unittest 5418 { 5419 import std.math.operations : isClose; 5420 import std.typecons : tuple; 5421 5422 enum foo = "a + 0.5 * b"; 5423 auto r = [0, 1, 2, 3]; 5424 auto r1 = r.cumulativeFold!foo; 5425 auto r2 = r.cumulativeFold!(foo, foo); 5426 assert(isClose(r1, [0, 0.5, 1.5, 3])); 5427 assert(isClose(r2.map!"a[0]", [0, 0.5, 1.5, 3])); 5428 assert(isClose(r2.map!"a[1]", [0, 0.5, 1.5, 3])); 5429 } 5430 5431 @safe unittest 5432 { 5433 import std.algorithm.comparison : equal, max, min; 5434 import std.array : array; 5435 import std.typecons : tuple; 5436 5437 //Seed is tuple of const. 5438 static auto minmaxElement(alias F = min, alias G = max, R)(in R range) 5439 @safe pure nothrow 5440 if (isInputRange!R) 5441 { 5442 return range.cumulativeFold!(F, G)(tuple(ElementType!R.max, ElementType!R.min)); 5443 } 5444 5445 assert(minmaxElement([1, 2, 3]).equal([tuple(1, 1), tuple(1, 2), tuple(1, 3)])); 5446 } 5447 5448 // https://issues.dlang.org/show_bug.cgi?id=12569 5449 @safe unittest 5450 { 5451 import std.algorithm.comparison : equal, max, min; 5452 import std.typecons : tuple; 5453 5454 dchar c = 'a'; 5455 5456 assert(cumulativeFold!(min, max)("hello", tuple(c, c)).equal([tuple('a', 'h'), 5457 tuple('a', 'h'), tuple('a', 'l'), tuple('a', 'l'), tuple('a', 'o')])); 5458 static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c)))); 5459 static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c, c, c)))); 5460 5461 //"Seed dchar should be a Tuple" 5462 static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", c))); 5463 //"Seed (dchar) does not have the correct amount of fields (should be 2)" 5464 static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c)))); 5465 //"Seed (dchar, dchar, dchar) does not have the correct amount of fields (should be 2)" 5466 static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c, c, c)))); 5467 //"Incompatible function/seed/element: all(alias pred = "a")/int/dchar" 5468 static assert(!__traits(compiles, cumulativeFold!all("hello", 1))); 5469 static assert(!__traits(compiles, cumulativeFold!(all, all)("hello", tuple(1, 1)))); 5470 } 5471 5472 // https://issues.dlang.org/show_bug.cgi?id=13304 5473 @safe unittest 5474 { 5475 int[] data; 5476 assert(data.cumulativeFold!((a, b) => a + b).empty); 5477 } 5478 5479 @safe unittest 5480 { 5481 import std.algorithm.comparison : equal; 5482 import std.internal.test.dummyrange : AllDummyRanges, propagatesLength, 5483 propagatesRangeType, RangeType; 5484 5485 foreach (DummyType; AllDummyRanges) 5486 { 5487 DummyType d; 5488 auto m = d.cumulativeFold!"a * b"; 5489 5490 static assert(propagatesLength!(typeof(m), DummyType)); 5491 static if (DummyType.rt <= RangeType.Forward) 5492 static assert(propagatesRangeType!(typeof(m), DummyType)); 5493 5494 assert(m.equal([1, 2, 6, 24, 120, 720, 5040, 40_320, 362_880, 3_628_800])); 5495 } 5496 } 5497 5498 // splitter 5499 /** 5500 Lazily splits a range using an element or range as a separator. 5501 Separator ranges can be any narrow string type or sliceable range type. 5502 5503 Two adjacent separators are considered to surround an empty element in 5504 the split range. Use `filter!(a => !a.empty)` on the result to compress 5505 empty elements. 5506 5507 The predicate is passed to $(REF binaryFun, std,functional) and accepts 5508 any callable function that can be executed via `pred(element, s)`. 5509 5510 Note: 5511 If splitting a string on whitespace and token compression is desired, 5512 consider using the `splitter(r)` overload. 5513 5514 Params: 5515 pred = The predicate for comparing each element with the separator, 5516 defaulting to `"a == b"`. 5517 r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be 5518 split. Must support slicing and `.length` or be a narrow string type. 5519 s = The element (or range) to be treated as the separator 5520 between range segments to be split. 5521 keepSeparators = The flag for deciding if the separators are kept 5522 5523 Constraints: 5524 The predicate `pred` needs to accept an element of `r` and the 5525 separator `s`. 5526 5527 Returns: 5528 An input range of the subranges of elements between separators. If `r` 5529 is a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) 5530 or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives), 5531 the returned range will be likewise. 5532 When a range is used a separator, bidirectionality isn't possible. 5533 5534 If `keepSeparators` is equal to `Yes.keepSeparators` the output will also contain the 5535 separators. 5536 5537 If an empty range is given, the result is an empty range. If a range with 5538 one separator is given, the result is a range with two empty elements. 5539 5540 See_Also: 5541 - $(REF _splitter, std,regex) for a version that splits using a regular expression defined separator. 5542 - $(REF _split, std,array) for a version that splits eagerly. 5543 - $(LREF splitWhen), which compares adjacent elements instead of element against separator. 5544 */ 5545 auto splitter(alias pred = "a == b", 5546 Flag!"keepSeparators" keepSeparators = No.keepSeparators, 5547 Range, 5548 Separator)(Range r, Separator s) 5549 if (is(typeof(binaryFun!pred(r.front, s)) : bool) 5550 && ((hasSlicing!Range && hasLength!Range) || isNarrowString!Range)) 5551 { 5552 import std.algorithm.searching : find; 5553 import std.conv : unsigned; 5554 5555 struct Result 5556 { 5557 private: 5558 Range _input; 5559 Separator _separator; 5560 // Do we need hasLength!Range? popFront uses _input.length... 5561 enum size_t _unComputed = size_t.max - 1, _atEnd = size_t.max; 5562 size_t _frontLength = _unComputed; 5563 size_t _backLength = _unComputed; 5564 5565 static if (isNarrowString!Range) 5566 { 5567 size_t _separatorLength; 5568 } 5569 else 5570 { 5571 enum _separatorLength = 1; 5572 } 5573 5574 static if (keepSeparators) 5575 { 5576 bool _wasSeparator = true; 5577 } 5578 5579 static if (isBidirectionalRange!Range) 5580 { 5581 size_t lastIndexOf(Range haystack, Separator needle) 5582 { 5583 import std.range : retro; 5584 auto r = haystack.retro().find!pred(needle); 5585 return r.retro().length - 1; 5586 } 5587 } 5588 5589 public: 5590 this(Range input, Separator separator) 5591 { 5592 _input = input; 5593 _separator = separator; 5594 5595 static if (isNarrowString!Range) 5596 { 5597 import std.utf : codeLength; 5598 5599 _separatorLength = codeLength!(ElementEncodingType!Range)(separator); 5600 } 5601 if (_input.empty) 5602 _frontLength = _atEnd; 5603 } 5604 5605 static if (isInfinite!Range) 5606 { 5607 enum bool empty = false; 5608 } 5609 else 5610 { 5611 @property bool empty() 5612 { 5613 return _frontLength == _atEnd; 5614 } 5615 } 5616 5617 @property Range front() 5618 { 5619 assert(!empty, "Attempting to fetch the front of an empty splitter."); 5620 static if (keepSeparators) 5621 { 5622 if (!_wasSeparator) 5623 { 5624 _frontLength = _separatorLength; 5625 _wasSeparator = true; 5626 } 5627 else if (_frontLength == _unComputed) 5628 { 5629 auto r = _input.find!pred(_separator); 5630 _frontLength = _input.length - r.length; 5631 _wasSeparator = false; 5632 } 5633 } 5634 else 5635 { 5636 if (_frontLength == _unComputed) 5637 { 5638 auto r = _input.find!pred(_separator); 5639 _frontLength = _input.length - r.length; 5640 } 5641 } 5642 return _input[0 .. _frontLength]; 5643 } 5644 5645 void popFront() 5646 { 5647 assert(!empty, "Attempting to popFront an empty splitter."); 5648 if (_frontLength == _unComputed) 5649 { 5650 front; 5651 } 5652 assert(_frontLength <= _input.length, "The front position must" 5653 ~ " not exceed the input.length"); 5654 static if (keepSeparators) 5655 { 5656 if (_frontLength == _input.length && !_wasSeparator) 5657 { 5658 _frontLength = _atEnd; 5659 5660 _backLength = _atEnd; 5661 } 5662 else 5663 { 5664 _input = _input[_frontLength .. _input.length]; 5665 _frontLength = _unComputed; 5666 } 5667 } 5668 else 5669 { 5670 if (_frontLength == _input.length) 5671 { 5672 // no more input and need to fetch => done 5673 _frontLength = _atEnd; 5674 5675 // Probably don't need this, but just for consistency: 5676 _backLength = _atEnd; 5677 } 5678 else 5679 { 5680 _input = _input[_frontLength + _separatorLength .. _input.length]; 5681 _frontLength = _unComputed; 5682 } 5683 } 5684 } 5685 5686 static if (isForwardRange!Range) 5687 { 5688 @property typeof(this) save() 5689 { 5690 auto ret = this; 5691 ret._input = _input.save; 5692 return ret; 5693 } 5694 } 5695 5696 static if (isBidirectionalRange!Range) 5697 { 5698 @property Range back() 5699 { 5700 assert(!empty, "Attempting to fetch the back of an empty splitter."); 5701 static if (keepSeparators) 5702 { 5703 if (!_wasSeparator) 5704 { 5705 _backLength = _separatorLength; 5706 _wasSeparator = true; 5707 } 5708 else if (_backLength == _unComputed) 5709 { 5710 immutable lastIndex = lastIndexOf(_input, _separator); 5711 if (lastIndex == -1) 5712 { 5713 _backLength = _input.length; 5714 } 5715 else 5716 { 5717 _backLength = _input.length - lastIndex - 1; 5718 } 5719 _wasSeparator = false; 5720 } 5721 } 5722 else 5723 { 5724 if (_backLength == _unComputed) 5725 { 5726 immutable lastIndex = lastIndexOf(_input, _separator); 5727 if (lastIndex == -1) 5728 { 5729 _backLength = _input.length; 5730 } 5731 else 5732 { 5733 _backLength = _input.length - lastIndex - 1; 5734 } 5735 } 5736 } 5737 return _input[_input.length - _backLength .. _input.length]; 5738 } 5739 5740 void popBack() 5741 { 5742 assert(!empty, "Attempting to popBack an empty splitter."); 5743 if (_backLength == _unComputed) 5744 { 5745 // evaluate back to make sure it's computed 5746 back; 5747 } 5748 assert(_backLength <= _input.length, "The end index must not" 5749 ~ " exceed the length of the input"); 5750 static if (keepSeparators) 5751 { 5752 if (_backLength == _input.length && !_wasSeparator) 5753 { 5754 _frontLength = _atEnd; 5755 _backLength = _atEnd; 5756 } 5757 else 5758 { 5759 _input = _input[0 .. _input.length - _backLength]; 5760 _backLength = _unComputed; 5761 } 5762 } 5763 else 5764 { 5765 if (_backLength == _input.length) 5766 { 5767 // no more input and need to fetch => done 5768 _frontLength = _atEnd; 5769 _backLength = _atEnd; 5770 } 5771 else 5772 { 5773 _input = _input[0 .. _input.length - _backLength - _separatorLength]; 5774 _backLength = _unComputed; 5775 } 5776 } 5777 } 5778 } 5779 } 5780 5781 return Result(r, s); 5782 } 5783 5784 /// Basic splitting with characters and numbers. 5785 @safe unittest 5786 { 5787 import std.algorithm.comparison : equal; 5788 5789 assert("a|bc|def".splitter('|').equal([ "a", "bc", "def" ])); 5790 5791 int[] a = [1, 0, 2, 3, 0, 4, 5, 6]; 5792 int[][] w = [ [1], [2, 3], [4, 5, 6] ]; 5793 assert(a.splitter(0).equal(w)); 5794 } 5795 5796 /// Basic splitting with characters and numbers and keeping sentinels. 5797 @safe unittest 5798 { 5799 import std.algorithm.comparison : equal; 5800 import std.typecons : Yes; 5801 5802 assert("a|bc|def".splitter!("a == b", Yes.keepSeparators)('|') 5803 .equal([ "a", "|", "bc", "|", "def" ])); 5804 5805 int[] a = [1, 0, 2, 3, 0, 4, 5, 6]; 5806 int[][] w = [ [1], [0], [2, 3], [0], [4, 5, 6] ]; 5807 assert(a.splitter!("a == b", Yes.keepSeparators)(0).equal(w)); 5808 } 5809 5810 /// Adjacent separators. 5811 @safe unittest 5812 { 5813 import std.algorithm.comparison : equal; 5814 5815 assert("|ab|".splitter('|').equal([ "", "ab", "" ])); 5816 assert("ab".splitter('|').equal([ "ab" ])); 5817 5818 assert("a|b||c".splitter('|').equal([ "a", "b", "", "c" ])); 5819 assert("hello world".splitter(' ').equal([ "hello", "", "world" ])); 5820 5821 auto a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; 5822 auto w = [ [1, 2], [], [3], [4, 5], [] ]; 5823 assert(a.splitter(0).equal(w)); 5824 } 5825 5826 /// Adjacent separators and keeping sentinels. 5827 @safe unittest 5828 { 5829 import std.algorithm.comparison : equal; 5830 import std.typecons : Yes; 5831 5832 assert("|ab|".splitter!("a == b", Yes.keepSeparators)('|') 5833 .equal([ "", "|", "ab", "|", "" ])); 5834 assert("ab".splitter!("a == b", Yes.keepSeparators)('|') 5835 .equal([ "ab" ])); 5836 5837 assert("a|b||c".splitter!("a == b", Yes.keepSeparators)('|') 5838 .equal([ "a", "|", "b", "|", "", "|", "c" ])); 5839 assert("hello world".splitter!("a == b", Yes.keepSeparators)(' ') 5840 .equal([ "hello", " ", "", " ", "world" ])); 5841 5842 auto a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; 5843 auto w = [ [1, 2], [0], [], [0], [3], [0], [4, 5], [0], [] ]; 5844 assert(a.splitter!("a == b", Yes.keepSeparators)(0).equal(w)); 5845 } 5846 5847 /// Empty and separator-only ranges. 5848 @safe unittest 5849 { 5850 import std.algorithm.comparison : equal; 5851 import std.range : empty; 5852 5853 assert("".splitter('|').empty); 5854 assert("|".splitter('|').equal([ "", "" ])); 5855 assert("||".splitter('|').equal([ "", "", "" ])); 5856 } 5857 5858 /// Empty and separator-only ranges and keeping sentinels. 5859 @safe unittest 5860 { 5861 import std.algorithm.comparison : equal; 5862 import std.typecons : Yes; 5863 import std.range : empty; 5864 5865 assert("".splitter!("a == b", Yes.keepSeparators)('|').empty); 5866 assert("|".splitter!("a == b", Yes.keepSeparators)('|') 5867 .equal([ "", "|", "" ])); 5868 assert("||".splitter!("a == b", Yes.keepSeparators)('|') 5869 .equal([ "", "|", "", "|", "" ])); 5870 } 5871 5872 /// Use a range for splitting 5873 @safe unittest 5874 { 5875 import std.algorithm.comparison : equal; 5876 5877 assert("a=>bc=>def".splitter("=>").equal([ "a", "bc", "def" ])); 5878 assert("a|b||c".splitter("||").equal([ "a|b", "c" ])); 5879 assert("hello world".splitter(" ").equal([ "hello", "world" ])); 5880 5881 int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; 5882 int[][] w = [ [1, 2], [3, 0, 4, 5, 0] ]; 5883 assert(a.splitter([0, 0]).equal(w)); 5884 5885 a = [ 0, 0 ]; 5886 assert(a.splitter([0, 0]).equal([ (int[]).init, (int[]).init ])); 5887 5888 a = [ 0, 0, 1 ]; 5889 assert(a.splitter([0, 0]).equal([ [], [1] ])); 5890 } 5891 5892 /// Use a range for splitting 5893 @safe unittest 5894 { 5895 import std.algorithm.comparison : equal; 5896 import std.typecons : Yes; 5897 5898 assert("a=>bc=>def".splitter!("a == b", Yes.keepSeparators)("=>") 5899 .equal([ "a", "=>", "bc", "=>", "def" ])); 5900 assert("a|b||c".splitter!("a == b", Yes.keepSeparators)("||") 5901 .equal([ "a|b", "||", "c" ])); 5902 assert("hello world".splitter!("a == b", Yes.keepSeparators)(" ") 5903 .equal([ "hello", " ", "world" ])); 5904 5905 int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; 5906 int[][] w = [ [1, 2], [0, 0], [3, 0, 4, 5, 0] ]; 5907 assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0]).equal(w)); 5908 5909 a = [ 0, 0 ]; 5910 assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0]) 5911 .equal([ (int[]).init, [0, 0], (int[]).init ])); 5912 5913 a = [ 0, 0, 1 ]; 5914 assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0]) 5915 .equal([ [], [0, 0], [1] ])); 5916 } 5917 5918 /// Custom predicate functions. 5919 @safe unittest 5920 { 5921 import std.algorithm.comparison : equal; 5922 import std.ascii : toLower; 5923 5924 assert("abXcdxef".splitter!"a.toLower == b"('x').equal( 5925 [ "ab", "cd", "ef" ])); 5926 5927 auto w = [ [0], [1], [2] ]; 5928 assert(w.splitter!"a.front == b"(1).equal([ [[0]], [[2]] ])); 5929 } 5930 5931 /// Custom predicate functions. 5932 @safe unittest 5933 { 5934 import std.algorithm.comparison : equal; 5935 import std.typecons : Yes; 5936 import std.ascii : toLower; 5937 5938 assert("abXcdxef".splitter!("a.toLower == b", Yes.keepSeparators)('x') 5939 .equal([ "ab", "X", "cd", "x", "ef" ])); 5940 5941 auto w = [ [0], [1], [2] ]; 5942 assert(w.splitter!("a.front == b", Yes.keepSeparators)(1) 5943 .equal([ [[0]], [[1]], [[2]] ])); 5944 } 5945 5946 /// Leading separators, trailing separators, or no separators. 5947 @safe unittest 5948 { 5949 import std.algorithm.comparison : equal; 5950 5951 assert("|ab|".splitter('|').equal([ "", "ab", "" ])); 5952 assert("ab".splitter('|').equal([ "ab" ])); 5953 } 5954 5955 /// Leading separators, trailing separators, or no separators. 5956 @safe unittest 5957 { 5958 import std.algorithm.comparison : equal; 5959 import std.typecons : Yes; 5960 5961 assert("|ab|".splitter!("a == b", Yes.keepSeparators)('|') 5962 .equal([ "", "|", "ab", "|", "" ])); 5963 assert("ab".splitter!("a == b", Yes.keepSeparators)('|') 5964 .equal([ "ab" ])); 5965 } 5966 5967 /// Splitter returns bidirectional ranges if the delimiter is a single element 5968 @safe unittest 5969 { 5970 import std.algorithm.comparison : equal; 5971 import std.range : retro; 5972 assert("a|bc|def".splitter('|').retro.equal([ "def", "bc", "a" ])); 5973 } 5974 5975 /// Splitter returns bidirectional ranges if the delimiter is a single element 5976 @safe unittest 5977 { 5978 import std.algorithm.comparison : equal; 5979 import std.typecons : Yes; 5980 import std.range : retro; 5981 assert("a|bc|def".splitter!("a == b", Yes.keepSeparators)('|') 5982 .retro.equal([ "def", "|", "bc", "|", "a" ])); 5983 } 5984 5985 @safe unittest 5986 { 5987 import std.algorithm; 5988 import std.array : array; 5989 import std.internal.test.dummyrange; 5990 import std.range : retro; 5991 5992 assert(equal(splitter("hello world", ' '), [ "hello", "", "world" ])); 5993 assert(equal(splitter("žlutoučkýřkůň", 'ř'), [ "žlutoučký", "kůň" ])); 5994 int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; 5995 int[][] w = [ [1, 2], [], [3], [4, 5], [] ]; 5996 static assert(isForwardRange!(typeof(splitter(a, 0)))); 5997 5998 assert(equal(splitter(a, 0), w)); 5999 a = null; 6000 assert(equal(splitter(a, 0), (int[][]).init)); 6001 a = [ 0 ]; 6002 assert(equal(splitter(a, 0), [ (int[]).init, (int[]).init ][])); 6003 a = [ 0, 1 ]; 6004 assert(equal(splitter(a, 0), [ [], [1] ])); 6005 assert(equal(splitter(a, 0), [ [], [1] ][])); 6006 6007 // Thoroughly exercise the bidirectional stuff. 6008 auto str = "abc abcd abcde ab abcdefg abcdefghij ab ac ar an at ada"; 6009 assert(equal( 6010 retro(splitter(str, 'a')), 6011 retro(array(splitter(str, 'a'))) 6012 )); 6013 6014 // Test interleaving front and back. 6015 auto split = splitter(str, 'a'); 6016 assert(split.front == ""); 6017 assert(split.back == ""); 6018 split.popBack(); 6019 assert(split.back == "d"); 6020 split.popFront(); 6021 assert(split.front == "bc "); 6022 assert(split.back == "d"); 6023 split.popFront(); 6024 split.popBack(); 6025 assert(split.back == "t "); 6026 split.popBack(); 6027 split.popBack(); 6028 split.popFront(); 6029 split.popFront(); 6030 assert(split.front == "b "); 6031 assert(split.back == "r "); 6032 6033 // https://issues.dlang.org/show_bug.cgi?id=4408 6034 foreach (DummyType; AllDummyRanges) 6035 { 6036 static if (isRandomAccessRange!DummyType) 6037 { 6038 static assert(isBidirectionalRange!DummyType); 6039 DummyType d; 6040 auto s = splitter(d, 5); 6041 assert(equal(s.front, [1,2,3,4])); 6042 assert(equal(s.back, [6,7,8,9,10])); 6043 6044 auto s2 = splitter(d, [4, 5]); 6045 assert(equal(s2.front, [1,2,3])); 6046 } 6047 } 6048 } 6049 @safe unittest 6050 { 6051 import std.algorithm; 6052 import std.range; 6053 auto L = retro(iota(1L, 10L)); 6054 auto s = splitter(L, 5L); 6055 assert(equal(s.front, [9L, 8L, 7L, 6L])); 6056 s.popFront(); 6057 assert(equal(s.front, [4L, 3L, 2L, 1L])); 6058 s.popFront(); 6059 assert(s.empty); 6060 } 6061 6062 // https://issues.dlang.org/show_bug.cgi?id=18470 6063 @safe unittest 6064 { 6065 import std.algorithm.comparison : equal; 6066 6067 const w = [[0], [1], [2]]; 6068 assert(w.splitter!((a, b) => a.front() == b)(1).equal([[[0]], [[2]]])); 6069 } 6070 6071 // https://issues.dlang.org/show_bug.cgi?id=18470 6072 @safe unittest 6073 { 6074 import std.algorithm.comparison : equal; 6075 import std.ascii : toLower; 6076 6077 assert("abXcdxef".splitter!"a.toLower == b"('x').equal(["ab", "cd", "ef"])); 6078 assert("abXcdxef".splitter!((a, b) => a.toLower == b)('x').equal(["ab", "cd", "ef"])); 6079 } 6080 6081 /// ditto 6082 auto splitter(alias pred = "a == b", 6083 Flag!"keepSeparators" keepSeparators = No.keepSeparators, 6084 Range, 6085 Separator)(Range r, Separator s) 6086 if (is(typeof(binaryFun!pred(r.front, s.front)) : bool) 6087 && (hasSlicing!Range || isNarrowString!Range) 6088 && isForwardRange!Separator 6089 && (hasLength!Separator || isNarrowString!Separator)) 6090 { 6091 import std.algorithm.searching : find; 6092 import std.conv : unsigned; 6093 6094 static struct Result 6095 { 6096 private: 6097 Range _input; 6098 Separator _separator; 6099 // _frontLength == size_t.max means empty 6100 size_t _frontLength = size_t.max; 6101 6102 static if (keepSeparators) 6103 { 6104 bool _wasSeparator = true; 6105 } 6106 6107 @property auto separatorLength() { return _separator.length; } 6108 6109 void ensureFrontLength() 6110 { 6111 if (_frontLength != _frontLength.max) return; 6112 static if (keepSeparators) 6113 { 6114 assert(!_input.empty || _wasSeparator, "The input must not be empty"); 6115 if (_wasSeparator) 6116 { 6117 _frontLength = _input.length - 6118 find!pred(_input, _separator).length; 6119 _wasSeparator = false; 6120 } 6121 else 6122 { 6123 _frontLength = separatorLength(); 6124 _wasSeparator = true; 6125 } 6126 } 6127 else 6128 { 6129 assert(!_input.empty, "The input must not be empty"); 6130 // compute front length 6131 _frontLength = (_separator.empty) ? 1 : 6132 _input.length - find!pred(_input, _separator).length; 6133 } 6134 } 6135 6136 public: 6137 this(Range input, Separator separator) 6138 { 6139 _input = input; 6140 _separator = separator; 6141 } 6142 6143 @property Range front() 6144 { 6145 assert(!empty, "Attempting to fetch the front of an empty splitter."); 6146 ensureFrontLength(); 6147 return _input[0 .. _frontLength]; 6148 } 6149 6150 static if (isInfinite!Range) 6151 { 6152 enum bool empty = false; // Propagate infiniteness 6153 } 6154 else 6155 { 6156 @property bool empty() 6157 { 6158 static if (keepSeparators) 6159 { 6160 return _frontLength == size_t.max && _input.empty && !_wasSeparator; 6161 } 6162 else 6163 { 6164 return _frontLength == size_t.max && _input.empty; 6165 } 6166 } 6167 } 6168 6169 void popFront() 6170 { 6171 assert(!empty, "Attempting to popFront an empty splitter."); 6172 ensureFrontLength(); 6173 6174 static if (keepSeparators) 6175 { 6176 _input = _input[_frontLength .. _input.length]; 6177 } 6178 else 6179 { 6180 if (_frontLength == _input.length) 6181 { 6182 // done, there's no separator in sight 6183 _input = _input[_frontLength .. _frontLength]; 6184 _frontLength = _frontLength.max; 6185 return; 6186 } 6187 if (_frontLength + separatorLength == _input.length) 6188 { 6189 // Special case: popping the first-to-last item; there is 6190 // an empty item right after this. 6191 _input = _input[_input.length .. _input.length]; 6192 _frontLength = 0; 6193 return; 6194 } 6195 // Normal case, pop one item and the separator, get ready for 6196 // reading the next item 6197 _input = _input[_frontLength + separatorLength .. _input.length]; 6198 } 6199 // mark _frontLength as uninitialized 6200 _frontLength = _frontLength.max; 6201 } 6202 6203 static if (isForwardRange!Range) 6204 { 6205 @property typeof(this) save() 6206 { 6207 auto ret = this; 6208 ret._input = _input.save; 6209 return ret; 6210 } 6211 } 6212 } 6213 6214 return Result(r, s); 6215 } 6216 6217 @safe unittest 6218 { 6219 import std.algorithm.comparison : equal; 6220 import std.typecons : Tuple; 6221 6222 alias C = Tuple!(int, "x", int, "y"); 6223 auto a = [C(1,0), C(2,0), C(3,1), C(4,0)]; 6224 assert(equal(splitter!"a.x == b"(a, [2, 3]), [ [C(1,0)], [C(4,0)] ])); 6225 } 6226 6227 @safe unittest 6228 { 6229 import std.algorithm.comparison : equal; 6230 import std.array : split; 6231 import std.conv : text; 6232 6233 auto s = ",abc, de, fg,hi,"; 6234 auto sp0 = splitter(s, ','); 6235 assert(equal(sp0, ["", "abc", " de", " fg", "hi", ""][])); 6236 6237 auto s1 = ", abc, de, fg, hi, "; 6238 auto sp1 = splitter(s1, ", "); 6239 assert(equal(sp1, ["", "abc", "de", " fg", "hi", ""][])); 6240 static assert(isForwardRange!(typeof(sp1))); 6241 6242 int[] a = [ 1, 2, 0, 3, 0, 4, 5, 0 ]; 6243 int[][] w = [ [1, 2], [3], [4, 5], [] ]; 6244 uint i; 6245 foreach (e; splitter(a, 0)) 6246 { 6247 assert(i < w.length); 6248 assert(e == w[i++]); 6249 } 6250 assert(i == w.length); 6251 6252 wstring names = ",peter,paul,jerry,"; 6253 auto words = split(names, ","); 6254 assert(walkLength(words) == 5, text(walkLength(words))); 6255 } 6256 6257 @safe unittest 6258 { 6259 int[][] a = [ [1], [2], [0], [3], [0], [4], [5], [0] ]; 6260 int[][][] w = [ [[1], [2]], [[3]], [[4], [5]], [] ]; 6261 uint i; 6262 foreach (e; splitter!"a.front == 0"(a, 0)) 6263 { 6264 assert(i < w.length); 6265 assert(e == w[i++]); 6266 } 6267 assert(i == w.length); 6268 } 6269 6270 @safe unittest 6271 { 6272 import std.algorithm.comparison : equal; 6273 auto s6 = ","; 6274 auto sp6 = splitter(s6, ','); 6275 foreach (e; sp6) {} 6276 assert(equal(sp6, ["", ""][])); 6277 } 6278 6279 // https://issues.dlang.org/show_bug.cgi?id=10773 6280 @safe unittest 6281 { 6282 import std.algorithm.comparison : equal; 6283 6284 auto s = splitter("abc", ""); 6285 assert(s.equal(["a", "b", "c"])); 6286 } 6287 6288 @safe unittest 6289 { 6290 import std.algorithm.comparison : equal; 6291 6292 // Test by-reference separator 6293 static class RefSep { 6294 @safe: 6295 string _impl; 6296 this(string s) { _impl = s; } 6297 @property empty() { return _impl.empty; } 6298 @property auto front() { return _impl.front; } 6299 void popFront() { _impl = _impl[1..$]; } 6300 @property RefSep save() scope { return new RefSep(_impl); } 6301 @property auto length() { return _impl.length; } 6302 } 6303 auto sep = new RefSep("->"); 6304 auto data = "i->am->pointing"; 6305 auto words = splitter(data, sep); 6306 assert(words.equal([ "i", "am", "pointing" ])); 6307 } 6308 6309 /++ 6310 Lazily splits a range `r` whenever a predicate `isTerminator` returns true for an element. 6311 6312 As above, two adjacent separators are considered to surround an empty element in 6313 the split range. 6314 6315 Params: 6316 r = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to be 6317 split. 6318 isTerminator = A $(REF_ALTTEXT unary, unaryFun, std,functional) predicate for 6319 deciding where to split the range. 6320 6321 Returns: 6322 A forward range of slices of the original range split by whitespace. 6323 +/ 6324 auto splitter(alias isTerminator, Range)(Range r) 6325 if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(r.front)))) 6326 { 6327 return SplitterResult!(unaryFun!isTerminator, Range)(r); 6328 } 6329 6330 /// 6331 @safe unittest 6332 { 6333 import std.ascii : isWhite; 6334 import std.algorithm.comparison : equal; 6335 import std.algorithm.iteration : splitter; 6336 6337 string str = "Hello World\t!"; 6338 assert(str.splitter!(isWhite).equal(["Hello", "World", "!"])); 6339 } 6340 6341 /// 6342 @safe unittest 6343 { 6344 import std.algorithm.comparison : equal; 6345 import std.range.primitives : front; 6346 6347 assert(equal(splitter!(a => a == '|')("a|bc|def"), [ "a", "bc", "def" ])); 6348 assert(equal(splitter!(a => a == ' ')("hello world"), [ "hello", "", "world" ])); 6349 6350 int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; 6351 int[][] w = [ [1, 2], [], [3], [4, 5], [] ]; 6352 assert(equal(splitter!(a => a == 0)(a), w)); 6353 6354 a = [ 0 ]; 6355 assert(equal(splitter!(a => a == 0)(a), [ (int[]).init, (int[]).init ])); 6356 6357 a = [ 0, 1 ]; 6358 assert(equal(splitter!(a => a == 0)(a), [ [], [1] ])); 6359 6360 w = [ [0], [1], [2] ]; 6361 assert(equal(splitter!(a => a.front == 1)(w), [ [[0]], [[2]] ])); 6362 } 6363 6364 private struct SplitterResult(alias isTerminator, Range) 6365 { 6366 import std.algorithm.searching : find; 6367 enum fullSlicing = (hasLength!Range && hasSlicing!Range) || isSomeString!Range; 6368 6369 private Range _input; 6370 private size_t _end = 0; 6371 static if (!fullSlicing) 6372 private Range _next; 6373 6374 private void findTerminator() 6375 { 6376 static if (fullSlicing) 6377 { 6378 auto r = find!isTerminator(_input.save); 6379 _end = _input.length - r.length; 6380 } 6381 else 6382 for ( _end = 0; !_next.empty ; _next.popFront) 6383 { 6384 if (isTerminator(_next.front)) 6385 break; 6386 ++_end; 6387 } 6388 } 6389 6390 this(Range input) 6391 { 6392 _input = input; 6393 static if (!fullSlicing) 6394 _next = _input.save; 6395 6396 if (!_input.empty) 6397 findTerminator(); 6398 else 6399 _end = size_t.max; 6400 } 6401 6402 static if (fullSlicing) 6403 { 6404 private this(Range input, size_t end) 6405 { 6406 _input = input; 6407 _end = end; 6408 } 6409 } 6410 else 6411 { 6412 private this(Range input, size_t end, Range next) 6413 { 6414 _input = input; 6415 _end = end; 6416 _next = next; 6417 } 6418 } 6419 6420 static if (isInfinite!Range) 6421 { 6422 enum bool empty = false; // Propagate infiniteness. 6423 } 6424 else 6425 { 6426 @property bool empty() 6427 { 6428 return _end == size_t.max; 6429 } 6430 } 6431 6432 @property auto front() 6433 { 6434 version (assert) 6435 { 6436 import core.exception : RangeError; 6437 if (empty) 6438 throw new RangeError(); 6439 } 6440 static if (fullSlicing) 6441 return _input[0 .. _end]; 6442 else 6443 { 6444 import std.range : takeExactly; 6445 return _input.takeExactly(_end); 6446 } 6447 } 6448 6449 void popFront() 6450 { 6451 version (assert) 6452 { 6453 import core.exception : RangeError; 6454 if (empty) 6455 throw new RangeError(); 6456 } 6457 6458 static if (fullSlicing) 6459 { 6460 _input = _input[_end .. _input.length]; 6461 if (_input.empty) 6462 { 6463 _end = size_t.max; 6464 return; 6465 } 6466 _input.popFront(); 6467 } 6468 else 6469 { 6470 if (_next.empty) 6471 { 6472 _input = _next; 6473 _end = size_t.max; 6474 return; 6475 } 6476 _next.popFront(); 6477 _input = _next.save; 6478 } 6479 findTerminator(); 6480 } 6481 6482 @property typeof(this) save() 6483 { 6484 static if (fullSlicing) 6485 return SplitterResult(_input.save, _end); 6486 else 6487 return SplitterResult(_input.save, _end, _next.save); 6488 } 6489 } 6490 6491 @safe unittest 6492 { 6493 import std.algorithm.comparison : equal; 6494 import std.range : iota; 6495 6496 auto L = iota(1L, 10L); 6497 auto s = splitter(L, [5L, 6L]); 6498 assert(equal(s.front, [1L, 2L, 3L, 4L])); 6499 s.popFront(); 6500 assert(equal(s.front, [7L, 8L, 9L])); 6501 s.popFront(); 6502 assert(s.empty); 6503 } 6504 6505 @safe unittest 6506 { 6507 import std.algorithm.comparison : equal; 6508 import std.algorithm.internal : algoFormat; 6509 import std.internal.test.dummyrange; 6510 6511 void compare(string sentence, string[] witness) 6512 { 6513 auto r = splitter!"a == ' '"(sentence); 6514 assert(equal(r.save, witness), algoFormat("got: %(%s, %) expected: %(%s, %)", r, witness)); 6515 } 6516 6517 compare(" Mary has a little lamb. ", 6518 ["", "Mary", "", "has", "a", "little", "lamb.", "", "", ""]); 6519 compare("Mary has a little lamb. ", 6520 ["Mary", "", "has", "a", "little", "lamb.", "", "", ""]); 6521 compare("Mary has a little lamb.", 6522 ["Mary", "", "has", "a", "little", "lamb."]); 6523 compare("", (string[]).init); 6524 compare(" ", ["", ""]); 6525 6526 static assert(isForwardRange!(typeof(splitter!"a == ' '"("ABC")))); 6527 6528 foreach (DummyType; AllDummyRanges) 6529 { 6530 static if (isRandomAccessRange!DummyType) 6531 { 6532 auto rangeSplit = splitter!"a == 5"(DummyType.init); 6533 assert(equal(rangeSplit.front, [1,2,3,4])); 6534 rangeSplit.popFront(); 6535 assert(equal(rangeSplit.front, [6,7,8,9,10])); 6536 } 6537 } 6538 } 6539 6540 @safe unittest 6541 { 6542 import std.algorithm.comparison : equal; 6543 import std.algorithm.internal : algoFormat; 6544 import std.range; 6545 6546 struct Entry 6547 { 6548 int low; 6549 int high; 6550 int[][] result; 6551 } 6552 Entry[] entries = [ 6553 Entry(0, 0, []), 6554 Entry(0, 1, [[0]]), 6555 Entry(1, 2, [[], []]), 6556 Entry(2, 7, [[2], [4], [6]]), 6557 Entry(1, 8, [[], [2], [4], [6], []]), 6558 ]; 6559 foreach ( entry ; entries ) 6560 { 6561 auto a = iota(entry.low, entry.high).filter!"true"(); 6562 auto b = splitter!"a%2"(a); 6563 assert(equal!equal(b.save, entry.result), algoFormat("got: %(%s, %) expected: %(%s, %)", b, entry.result)); 6564 } 6565 } 6566 6567 @safe unittest 6568 { 6569 import std.algorithm.comparison : equal; 6570 import std.uni : isWhite; 6571 6572 // https://issues.dlang.org/show_bug.cgi?id=6791 6573 assert(equal( 6574 splitter("là dove terminava quella valle"), 6575 ["là", "dove", "terminava", "quella", "valle"] 6576 )); 6577 assert(equal( 6578 splitter!(isWhite)("là dove terminava quella valle"), 6579 ["là", "dove", "terminava", "quella", "valle"] 6580 )); 6581 assert(equal(splitter!"a=='本'"("日本語"), ["日", "語"])); 6582 } 6583 6584 // https://issues.dlang.org/show_bug.cgi?id=18657 6585 pure @safe unittest 6586 { 6587 import std.algorithm.comparison : equal; 6588 import std.range : refRange; 6589 string s = "foobar"; 6590 auto r = refRange(&s).splitter!(c => c == 'b'); 6591 assert(equal!equal(r.save, ["foo", "ar"])); 6592 assert(equal!equal(r.save, ["foo", "ar"])); 6593 } 6594 6595 /++ 6596 Lazily splits the character-based range `s` into words, using whitespace as the 6597 delimiter. 6598 6599 This function is character-range specific and, contrary to 6600 `splitter!(std.uni.isWhite)`, runs of whitespace will be merged together 6601 (no empty tokens will be produced). 6602 6603 Params: 6604 s = The character-based range to be split. Must be a string, or a 6605 random-access range of character types. 6606 6607 Returns: 6608 An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of slices of 6609 the original range split by whitespace. 6610 +/ 6611 auto splitter(Range)(Range s) 6612 if (isSomeString!Range || 6613 isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range && 6614 !isConvertibleToString!Range && 6615 isSomeChar!(ElementEncodingType!Range)) 6616 { 6617 import std.algorithm.searching : find; 6618 static struct Result 6619 { 6620 private: 6621 import core.exception : RangeError; 6622 Range _s; 6623 size_t _frontLength; 6624 6625 void getFirst() 6626 { 6627 import std.uni : isWhite; 6628 import std.traits : Unqual; 6629 6630 static if (is(immutable ElementEncodingType!Range == immutable wchar) && 6631 is(immutable ElementType!Range == immutable dchar)) 6632 { 6633 // all unicode whitespace characters fit into a wchar. However, 6634 // this range is a wchar array, so we will treat it like a 6635 // wchar array instead of decoding each code point. 6636 _frontLength = _s.length; // default condition, no spaces 6637 foreach (i; 0 .. _s.length) 6638 if (isWhite(_s[i])) 6639 { 6640 _frontLength = i; 6641 break; 6642 } 6643 } 6644 else static if (is(immutable ElementType!Range == immutable dchar) || 6645 is(immutable ElementType!Range == immutable wchar)) 6646 { 6647 // dchar or wchar range, we can just use find. 6648 auto r = find!(isWhite)(_s.save); 6649 _frontLength = _s.length - r.length; 6650 } 6651 else 6652 { 6653 // need to decode the characters until we find a space. This is 6654 // ported from std.string.stripLeft. 6655 static import std.ascii; 6656 static import std.uni; 6657 import std.utf : decodeFront; 6658 6659 auto input = _s.save; 6660 size_t iLength = input.length; 6661 6662 while (!input.empty) 6663 { 6664 auto c = input.front; 6665 if (std.ascii.isASCII(c)) 6666 { 6667 if (std.ascii.isWhite(c)) 6668 break; 6669 input.popFront(); 6670 --iLength; 6671 } 6672 else 6673 { 6674 auto dc = decodeFront(input); 6675 if (std.uni.isWhite(dc)) 6676 break; 6677 iLength = input.length; 6678 } 6679 } 6680 6681 // sanity check 6682 assert(iLength <= _s.length, "The current index must not" 6683 ~ " exceed the length of the input"); 6684 6685 _frontLength = _s.length - iLength; 6686 } 6687 } 6688 6689 public: 6690 this(Range s) 6691 { 6692 import std.string : stripLeft; 6693 _s = s.stripLeft(); 6694 getFirst(); 6695 } 6696 6697 @property auto front() 6698 { 6699 version (assert) if (empty) throw new RangeError(); 6700 return _s[0 .. _frontLength]; 6701 } 6702 6703 void popFront() 6704 { 6705 import std.string : stripLeft; 6706 version (assert) if (empty) throw new RangeError(); 6707 _s = _s[_frontLength .. $].stripLeft(); 6708 getFirst(); 6709 } 6710 6711 @property bool empty() const 6712 { 6713 return _s.empty; 6714 } 6715 6716 @property inout(Result) save() inout @safe pure nothrow 6717 { 6718 return this; 6719 } 6720 } 6721 return Result(s); 6722 } 6723 6724 /// 6725 @safe pure unittest 6726 { 6727 import std.algorithm.comparison : equal; 6728 auto a = " a bcd ef gh "; 6729 assert(equal(splitter(a), ["a", "bcd", "ef", "gh"][])); 6730 } 6731 6732 @safe pure unittest 6733 { 6734 import std.algorithm.comparison : equal; 6735 import std.meta : AliasSeq; 6736 static foreach (S; AliasSeq!(string, wstring, dstring)) 6737 {{ 6738 import std.conv : to; 6739 S a = " a \u2028 bcd ef gh "; 6740 assert(equal(splitter(a), [to!S("a"), to!S("bcd"), to!S("ef"), to!S("gh")])); 6741 a = ""; 6742 assert(splitter(a).empty); 6743 }} 6744 6745 immutable string s = " a bcd ef gh "; 6746 assert(equal(splitter(s), ["a", "bcd", "ef", "gh"][])); 6747 } 6748 6749 @safe unittest 6750 { 6751 import std.conv : to; 6752 import std.string : strip; 6753 6754 // TDPL example, page 8 6755 uint[string] dictionary; 6756 char[][3] lines; 6757 lines[0] = "line one".dup; 6758 lines[1] = "line \ttwo".dup; 6759 lines[2] = "yah last line\ryah".dup; 6760 foreach (line; lines) 6761 { 6762 foreach (word; splitter(strip(line))) 6763 { 6764 if (word in dictionary) continue; // Nothing to do 6765 auto newID = dictionary.length; 6766 dictionary[to!string(word)] = cast(uint) newID; 6767 } 6768 } 6769 assert(dictionary.length == 5); 6770 assert(dictionary["line"]== 0); 6771 assert(dictionary["one"]== 1); 6772 assert(dictionary["two"]== 2); 6773 assert(dictionary["yah"]== 3); 6774 assert(dictionary["last"]== 4); 6775 6776 } 6777 6778 @safe unittest 6779 { 6780 // do it with byCodeUnit 6781 import std.conv : to; 6782 import std.string : strip; 6783 import std.utf : byCodeUnit; 6784 6785 alias BCU = typeof("abc".byCodeUnit()); 6786 6787 // TDPL example, page 8 6788 uint[BCU] dictionary; 6789 BCU[3] lines; 6790 lines[0] = "line one".byCodeUnit; 6791 lines[1] = "line \ttwo".byCodeUnit; 6792 lines[2] = "yah last line\ryah".byCodeUnit; 6793 foreach (line; lines) 6794 { 6795 foreach (word; splitter(strip(line))) 6796 { 6797 static assert(is(typeof(word) == BCU)); 6798 if (word in dictionary) continue; // Nothing to do 6799 auto newID = dictionary.length; 6800 dictionary[word] = cast(uint) newID; 6801 } 6802 } 6803 assert(dictionary.length == 5); 6804 assert(dictionary["line".byCodeUnit]== 0); 6805 assert(dictionary["one".byCodeUnit]== 1); 6806 assert(dictionary["two".byCodeUnit]== 2); 6807 assert(dictionary["yah".byCodeUnit]== 3); 6808 assert(dictionary["last".byCodeUnit]== 4); 6809 } 6810 6811 // https://issues.dlang.org/show_bug.cgi?id=19238 6812 @safe pure unittest 6813 { 6814 import std.utf : byCodeUnit; 6815 import std.algorithm.comparison : equal; 6816 auto range = "hello world".byCodeUnit.splitter; 6817 static assert(is(typeof(range.front()) == typeof("hello".byCodeUnit()))); 6818 assert(range.equal(["hello".byCodeUnit, "world".byCodeUnit])); 6819 6820 // test other space types, including unicode 6821 auto u = " a\t\v\r bcd\u3000 \u2028\t\nef\U00010001 gh"; 6822 assert(equal(splitter(u), ["a", "bcd", "ef\U00010001", "gh"][])); 6823 assert(equal(splitter(u.byCodeUnit), ["a".byCodeUnit, "bcd".byCodeUnit, 6824 "ef\U00010001".byCodeUnit, "gh".byCodeUnit][])); 6825 } 6826 6827 @safe unittest 6828 { 6829 import std.algorithm.comparison : equal; 6830 import std.algorithm.internal : algoFormat; 6831 import std.array : split; 6832 import std.conv : text; 6833 6834 // Check consistency: 6835 // All flavors of split should produce the same results 6836 foreach (input; [(int[]).init, 6837 [0], 6838 [0, 1, 0], 6839 [1, 1, 0, 0, 1, 1], 6840 ]) 6841 { 6842 foreach (s; [0, 1]) 6843 { 6844 auto result = split(input, s); 6845 6846 assert(equal(result, split(input, [s])), algoFormat(`"[%(%s,%)]"`, split(input, [s]))); 6847 //assert(equal(result, split(input, [s].filter!"true"()))); //Not yet implemented 6848 assert(equal(result, split!((a) => a == s)(input)), text(split!((a) => a == s)(input))); 6849 6850 //assert(equal!equal(result, split(input.filter!"true"(), s))); //Not yet implemented 6851 //assert(equal!equal(result, split(input.filter!"true"(), [s]))); //Not yet implemented 6852 //assert(equal!equal(result, split(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented 6853 assert(equal!equal(result, split!((a) => a == s)(input.filter!"true"()))); 6854 6855 assert(equal(result, splitter(input, s))); 6856 assert(equal(result, splitter(input, [s]))); 6857 //assert(equal(result, splitter(input, [s].filter!"true"()))); //Not yet implemented 6858 assert(equal(result, splitter!((a) => a == s)(input))); 6859 6860 //assert(equal!equal(result, splitter(input.filter!"true"(), s))); //Not yet implemented 6861 //assert(equal!equal(result, splitter(input.filter!"true"(), [s]))); //Not yet implemented 6862 //assert(equal!equal(result, splitter(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented 6863 assert(equal!equal(result, splitter!((a) => a == s)(input.filter!"true"()))); 6864 } 6865 } 6866 foreach (input; [string.init, 6867 " ", 6868 " hello ", 6869 "hello hello", 6870 " hello what heck this ? " 6871 ]) 6872 { 6873 foreach (s; [' ', 'h']) 6874 { 6875 auto result = split(input, s); 6876 6877 assert(equal(result, split(input, [s]))); 6878 //assert(equal(result, split(input, [s].filter!"true"()))); //Not yet implemented 6879 assert(equal(result, split!((a) => a == s)(input))); 6880 6881 //assert(equal!equal(result, split(input.filter!"true"(), s))); //Not yet implemented 6882 //assert(equal!equal(result, split(input.filter!"true"(), [s]))); //Not yet implemented 6883 //assert(equal!equal(result, split(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented 6884 assert(equal!equal(result, split!((a) => a == s)(input.filter!"true"()))); 6885 6886 assert(equal(result, splitter(input, s))); 6887 assert(equal(result, splitter(input, [s]))); 6888 //assert(equal(result, splitter(input, [s].filter!"true"()))); //Not yet implemented 6889 assert(equal(result, splitter!((a) => a == s)(input))); 6890 6891 //assert(equal!equal(result, splitter(input.filter!"true"(), s))); //Not yet implemented 6892 //assert(equal!equal(result, splitter(input.filter!"true"(), [s]))); //Not yet implemented 6893 //assert(equal!equal(result, splitter(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented 6894 assert(equal!equal(result, splitter!((a) => a == s)(input.filter!"true"()))); 6895 } 6896 } 6897 } 6898 6899 // In same combinations substitute needs to calculate the auto-decoded length 6900 // of its needles 6901 private template hasDifferentAutodecoding(Range, Needles...) 6902 { 6903 import std.meta : anySatisfy; 6904 /* iff 6905 - the needles needs auto-decoding, but the incoming range doesn't (or vice versa) 6906 - both (range, needle) need auto-decoding and don't share the same common type 6907 */ 6908 enum needlesAreNarrow = anySatisfy!(isNarrowString, Needles); 6909 enum sourceIsNarrow = isNarrowString!Range; 6910 enum hasDifferentAutodecoding = sourceIsNarrow != needlesAreNarrow || 6911 (sourceIsNarrow && needlesAreNarrow && 6912 is(CommonType!(Range, Needles) == void)); 6913 } 6914 6915 @safe nothrow @nogc pure unittest 6916 { 6917 import std.meta : AliasSeq; // used for better clarity 6918 6919 static assert(!hasDifferentAutodecoding!(string, AliasSeq!(string, string))); 6920 static assert(!hasDifferentAutodecoding!(wstring, AliasSeq!(wstring, wstring))); 6921 static assert(!hasDifferentAutodecoding!(dstring, AliasSeq!(dstring, dstring))); 6922 6923 // the needles needs auto-decoding, but the incoming range doesn't (or vice versa) 6924 static assert(hasDifferentAutodecoding!(string, AliasSeq!(wstring, wstring))); 6925 static assert(hasDifferentAutodecoding!(string, AliasSeq!(dstring, dstring))); 6926 static assert(hasDifferentAutodecoding!(wstring, AliasSeq!(string, string))); 6927 static assert(hasDifferentAutodecoding!(wstring, AliasSeq!(dstring, dstring))); 6928 static assert(hasDifferentAutodecoding!(dstring, AliasSeq!(string, string))); 6929 static assert(hasDifferentAutodecoding!(dstring, AliasSeq!(wstring, wstring))); 6930 6931 // both (range, needle) need auto-decoding and don't share the same common type 6932 static foreach (T; AliasSeq!(string, wstring, dstring)) 6933 { 6934 static assert(hasDifferentAutodecoding!(T, AliasSeq!(wstring, string))); 6935 static assert(hasDifferentAutodecoding!(T, AliasSeq!(dstring, string))); 6936 static assert(hasDifferentAutodecoding!(T, AliasSeq!(wstring, dstring))); 6937 } 6938 } 6939 6940 // substitute 6941 /** 6942 Returns a range with all occurrences of `substs` in `r`. 6943 replaced with their substitution. 6944 6945 Single value replacements (`'ö'.substitute!('ä', 'a', 'ö', 'o', 'ü', 'u)`) are 6946 supported as well and in $(BIGOH 1). 6947 6948 Params: 6949 r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 6950 value = a single value which can be substituted in $(BIGOH 1) 6951 substs = a set of replacements/substitutions 6952 pred = the equality function to test if element(s) are equal to 6953 a substitution 6954 6955 Returns: a range with the substitutions replaced. 6956 6957 See_Also: 6958 $(REF replace, std, array) for an eager replace algorithm or 6959 $(REF translate, std, string), and $(REF tr, std, string) 6960 for string algorithms with translation tables. 6961 */ 6962 template substitute(substs...) 6963 if (substs.length >= 2 && isExpressions!substs) 6964 { 6965 import std.range.primitives : ElementType; 6966 import std.traits : CommonType; 6967 6968 static assert(!(substs.length & 1), "The number of substitution parameters must be even"); 6969 6970 /** 6971 Substitute single values with compile-time substitution mappings. 6972 Complexity: $(BIGOH 1) due to D's `switch` guaranteeing $(BIGOH 1); 6973 */ 6974 auto substitute(Value)(Value value) 6975 if (isInputRange!Value || !is(CommonType!(Value, typeof(substs[0])) == void)) 6976 { 6977 static if (isInputRange!Value) 6978 { 6979 static if (!is(CommonType!(ElementType!Value, typeof(substs[0])) == void)) 6980 { 6981 // Substitute single range elements with compile-time substitution mappings 6982 return value.map!(a => substitute(a)); 6983 } 6984 else static if (isInputRange!Value && 6985 !is(CommonType!(ElementType!Value, ElementType!(typeof(substs[0]))) == void)) 6986 { 6987 // not implemented yet, fallback to runtime variant for now 6988 return .substitute(value, substs); 6989 } 6990 else 6991 { 6992 static assert(0, `Compile-time substitutions must be elements or ranges of the same type of ` ~ 6993 Value.stringof ~ `.`); 6994 } 6995 } 6996 // Substitute single values with compile-time substitution mappings. 6997 else // static if (!is(CommonType!(Value, typeof(substs[0])) == void)) 6998 { 6999 switch (value) 7000 { 7001 static foreach (i; 0 .. substs.length / 2) 7002 case substs[2 * i]: 7003 return substs[2 * i + 1]; 7004 7005 default: return value; 7006 } 7007 } 7008 } 7009 } 7010 7011 /// ditto 7012 auto substitute(alias pred = (a, b) => a == b, R, Substs...)(R r, Substs substs) 7013 if (isInputRange!R && Substs.length >= 2 && !is(CommonType!(Substs) == void)) 7014 { 7015 import std.range.primitives : ElementType; 7016 import std.meta : allSatisfy; 7017 import std.traits : CommonType; 7018 7019 static assert(!(Substs.length & 1), "The number of substitution parameters must be even"); 7020 7021 enum n = Substs.length / 2; 7022 7023 // Substitute individual elements 7024 static if (!is(CommonType!(ElementType!R, Substs) == void)) 7025 { 7026 import std.functional : binaryFun; 7027 7028 // Imitate a value closure to be @nogc 7029 static struct ReplaceElement 7030 { 7031 private Substs substs; 7032 7033 this(Substs substs) 7034 { 7035 this.substs = substs; 7036 } 7037 7038 auto opCall(E)(E e) 7039 { 7040 static foreach (i; 0 .. n) 7041 if (binaryFun!pred(e, substs[2 * i])) 7042 return substs[2 * i + 1]; 7043 7044 return e; 7045 } 7046 } 7047 auto er = ReplaceElement(substs); 7048 return r.map!er; 7049 } 7050 // Substitute subranges 7051 else static if (!is(CommonType!(ElementType!R, ElementType!(Substs[0])) == void) && 7052 allSatisfy!(isForwardRange, Substs)) 7053 { 7054 import std.range : choose, take; 7055 import std.meta : Stride; 7056 7057 auto replaceElement(E)(E e) 7058 { 7059 alias ReturnA = typeof(e[0]); 7060 alias ReturnB = typeof(substs[0 .. 1].take(1)); 7061 7062 // 1-based index 7063 const auto hitNr = e[1]; 7064 switch (hitNr) 7065 { 7066 // no hit 7067 case 0: 7068 // use choose trick for non-common range 7069 static if (is(CommonType!(ReturnA, ReturnB) == void)) 7070 return choose(1, e[0], ReturnB.init); 7071 else 7072 return e[0]; 7073 7074 // all replacements 7075 static foreach (i; 0 .. n) 7076 case i + 1: 7077 // use choose trick for non-common ranges 7078 static if (is(CommonType!(ReturnA, ReturnB) == void)) 7079 return choose(0, e[0], substs[2 * i + 1].take(size_t.max)); 7080 else 7081 return substs[2 * i + 1].take(size_t.max); 7082 default: 7083 assert(0, "hitNr should always be found."); 7084 } 7085 } 7086 7087 alias Ins = Stride!(2, Substs); 7088 7089 static struct SubstituteSplitter 7090 { 7091 import std.range : drop; 7092 import std.typecons : Tuple; 7093 7094 private 7095 { 7096 typeof(R.init.drop(0)) rest; 7097 Ins needles; 7098 7099 typeof(R.init.take(0)) skip; // skip before next hit 7100 alias Hit = size_t; // 0 iff no hit, otherwise hit in needles[index-1] 7101 alias E = Tuple!(typeof(skip), Hit); 7102 Hit hitNr; // hit number: 0 means no hit, otherwise index+1 to needles that matched 7103 bool hasHit; // is there a replacement hit which should be printed? 7104 7105 enum hasDifferentAutodecoding = .hasDifferentAutodecoding!(typeof(rest), Ins); 7106 7107 // calculating the needle length for narrow strings might be expensive -> cache it 7108 static if (hasDifferentAutodecoding) 7109 ptrdiff_t[n] needleLengths = -1; 7110 } 7111 7112 this(R haystack, Ins needles) 7113 { 7114 this.rest = haystack.drop(0); 7115 this.needles = needles; 7116 if (!haystack.empty) 7117 { 7118 hasHit = true; 7119 popFront; 7120 } 7121 static if (hasNested!(typeof(skip))) 7122 skip = rest.take(0); 7123 } 7124 7125 /* If `skip` is non-empty, it's returned as (skip, 0) tuple 7126 otherwise a similar (<empty>, hitNr) tuple is returned. 7127 `replaceElement` maps based on the second item (`hitNr`). 7128 */ 7129 @property auto ref front() 7130 { 7131 assert(!empty, "Attempting to fetch the front of an empty substitute."); 7132 return !skip.empty ? E(skip, 0) : E(typeof(skip).init, hitNr); 7133 } 7134 7135 static if (isInfinite!R) 7136 enum empty = false; // propagate infiniteness 7137 else 7138 @property bool empty() 7139 { 7140 return skip.empty && !hasHit; 7141 } 7142 7143 /* If currently in a skipping phase => reset. 7144 Otherwise try to find the next occurrence of `needles` 7145 If valid match 7146 - if there are elements before the match, set skip with these elements 7147 (on the next popFront, the range will be in the skip state once) 7148 - `rest`: advance to the end of the match 7149 - set hasHit 7150 Otherwise skip to the end 7151 */ 7152 void popFront() 7153 { 7154 assert(!empty, "Attempting to popFront an empty substitute."); 7155 if (!skip.empty) 7156 { 7157 skip = typeof(skip).init; // jump over skip 7158 } 7159 else 7160 { 7161 import std.algorithm.searching : countUntil, find; 7162 7163 auto match = rest.find!pred(needles); 7164 7165 static if (needles.length >= 2) // variadic version of find (returns a tuple) 7166 { 7167 // find with variadic needles returns a (range, needleNr) tuple 7168 // needleNr is a 1-based index 7169 auto hitValue = match[0]; 7170 hitNr = match[1]; 7171 } 7172 else 7173 { 7174 // find with one needle returns the range 7175 auto hitValue = match; 7176 hitNr = match.empty ? 0 : 1; 7177 } 7178 7179 if (hitNr == 0) // no more hits 7180 { 7181 skip = rest.take(size_t.max); 7182 hasHit = false; 7183 rest = typeof(rest).init; 7184 } 7185 else 7186 { 7187 auto hitLength = size_t.max; 7188 switchL: switch (hitNr - 1) 7189 { 7190 static foreach (i; 0 .. n) 7191 { 7192 case i: 7193 static if (hasDifferentAutodecoding) 7194 { 7195 import std.utf : codeLength; 7196 7197 // cache calculated needle length 7198 if (needleLengths[i] != -1) 7199 hitLength = needleLengths[i]; 7200 else 7201 hitLength = needleLengths[i] = codeLength!dchar(needles[i]); 7202 } 7203 else 7204 { 7205 hitLength = needles[i].length; 7206 } 7207 break switchL; 7208 } 7209 default: 7210 assert(0, "hitNr should always be found"); 7211 } 7212 7213 const pos = rest.countUntil(hitValue); 7214 if (pos > 0) // match not at start of rest 7215 skip = rest.take(pos); 7216 7217 hasHit = true; 7218 7219 // iff the source range and the substitutions are narrow strings, 7220 // we can avoid calling the auto-decoding `popFront` (via drop) 7221 static if (isNarrowString!(typeof(hitValue)) && !hasDifferentAutodecoding) 7222 rest = hitValue[hitLength .. $]; 7223 else 7224 rest = hitValue.drop(hitLength); 7225 } 7226 } 7227 } 7228 } 7229 7230 // extract inputs 7231 Ins ins; 7232 static foreach (i; 0 .. n) 7233 ins[i] = substs[2 * i]; 7234 7235 return SubstituteSplitter(r, ins) 7236 .map!(a => replaceElement(a)) 7237 .joiner; 7238 } 7239 else 7240 { 7241 static assert(0, "The substitutions must either substitute a single element or a save-able subrange."); 7242 } 7243 } 7244 7245 /// 7246 @safe pure unittest 7247 { 7248 import std.algorithm.comparison : equal; 7249 7250 // substitute single elements 7251 assert("do_it".substitute('_', ' ').equal("do it")); 7252 7253 // substitute multiple, single elements 7254 assert("do_it".substitute('_', ' ', 7255 'd', 'g', 7256 'i', 't', 7257 't', 'o') 7258 .equal("go to")); 7259 7260 // substitute subranges 7261 assert("do_it".substitute("_", " ", 7262 "do", "done") 7263 .equal("done it")); 7264 7265 // substitution works for any ElementType 7266 int[] x = [1, 2, 3]; 7267 auto y = x.substitute(1, 0.1); 7268 assert(y.equal([0.1, 2, 3])); 7269 static assert(is(typeof(y.front) == double)); 7270 7271 import std.range : retro; 7272 assert([1, 2, 3].substitute(1, 0.1).retro.equal([3, 2, 0.1])); 7273 } 7274 7275 /// Use the faster compile-time overload 7276 @safe pure unittest 7277 { 7278 import std.algorithm.comparison : equal; 7279 7280 // substitute subranges of a range 7281 assert("apple_tree".substitute!("apple", "banana", 7282 "tree", "shrub").equal("banana_shrub")); 7283 7284 // substitute subranges of a range 7285 assert("apple_tree".substitute!('a', 'b', 7286 't', 'f').equal("bpple_free")); 7287 7288 // substitute values 7289 assert('a'.substitute!('a', 'b', 't', 'f') == 'b'); 7290 } 7291 7292 /// Multiple substitutes 7293 @safe pure unittest 7294 { 7295 import std.algorithm.comparison : equal; 7296 import std.range.primitives : ElementType; 7297 7298 int[3] x = [1, 2, 3]; 7299 auto y = x[].substitute(1, 0.1) 7300 .substitute(0.1, 0.2); 7301 static assert(is(typeof(y.front) == double)); 7302 assert(y.equal([0.2, 2, 3])); 7303 7304 auto z = "42".substitute('2', '3') 7305 .substitute('3', '1'); 7306 static assert(is(ElementType!(typeof(z)) == dchar)); 7307 assert(equal(z, "41")); 7308 } 7309 7310 // Test the first example with compile-time overloads 7311 @safe pure unittest 7312 { 7313 import std.algorithm.comparison : equal; 7314 7315 // substitute single elements 7316 assert("do_it".substitute!('_', ' ').equal("do it")); 7317 7318 // substitute multiple, single elements 7319 assert("do_it".substitute!('_', ' ', 7320 'd', 'g', 7321 'i', 't', 7322 't', 'o') 7323 .equal(`go to`)); 7324 7325 // substitute subranges 7326 assert("do_it".substitute!("_", " ", 7327 "do", "done") 7328 .equal("done it")); 7329 7330 // substitution works for any ElementType 7331 int[3] x = [1, 2, 3]; 7332 auto y = x[].substitute!(1, 0.1); 7333 assert(y.equal([0.1, 2, 3])); 7334 static assert(is(typeof(y.front) == double)); 7335 7336 import std.range : retro; 7337 assert([1, 2, 3].substitute!(1, 0.1).retro.equal([3, 2, 0.1])); 7338 } 7339 7340 // test infinite ranges 7341 @safe pure nothrow unittest 7342 { 7343 import std.algorithm.comparison : equal; 7344 import std.range : cycle, take; 7345 7346 int[] x = [1, 2, 3]; 7347 assert(x.cycle.substitute!(1, 0.1).take(4).equal([0.1, 2, 3, 0.1])); 7348 assert(x.cycle.substitute(1, 0.1).take(4).equal([0.1, 2, 3, 0.1])); 7349 } 7350 7351 // test infinite ranges 7352 @safe pure nothrow unittest 7353 { 7354 import std.algorithm.comparison : equal; 7355 import std.internal.test.dummyrange : AllDummyRanges; 7356 7357 foreach (R; AllDummyRanges) 7358 { 7359 assert(R.init 7360 .substitute!(2, 22, 3, 33, 5, 55, 9, 99) 7361 .equal([1, 22, 33, 4, 55, 6, 7, 8, 99, 10])); 7362 7363 assert(R.init 7364 .substitute(2, 22, 3, 33, 5, 55, 9, 99) 7365 .equal([1, 22, 33, 4, 55, 6, 7, 8, 99, 10])); 7366 } 7367 } 7368 7369 // test multiple replacements 7370 @safe pure unittest 7371 { 7372 import std.algorithm.comparison : equal; 7373 7374 assert("alpha.beta.gamma" 7375 .substitute("alpha", "1", 7376 "gamma", "3", 7377 "beta", "2").equal("1.2.3")); 7378 7379 assert("alpha.beta.gamma." 7380 .substitute("alpha", "1", 7381 "gamma", "3", 7382 "beta", "2").equal("1.2.3.")); 7383 7384 assert("beta.beta.beta" 7385 .substitute("alpha", "1", 7386 "gamma", "3", 7387 "beta", "2").equal("2.2.2")); 7388 7389 assert("alpha.alpha.alpha" 7390 .substitute("alpha", "1", 7391 "gamma", "3", 7392 "beta", "2").equal("1.1.1")); 7393 } 7394 7395 // test combination of subrange + element replacement 7396 @safe pure unittest 7397 { 7398 import std.algorithm.comparison : equal; 7399 7400 assert(("abcDe".substitute("a", "AA", 7401 "b", "DD") 7402 .substitute('A', 'y', 7403 'D', 'x', 7404 'e', '1')) 7405 .equal("yyxxcx1")); 7406 } 7407 7408 // test const + immutable storage groups 7409 @safe pure unittest 7410 { 7411 import std.algorithm.comparison : equal; 7412 7413 auto xyz_abc(T)(T value) 7414 { 7415 immutable a = "a"; 7416 const b = "b"; 7417 auto c = "c"; 7418 return value.substitute!("x", a, 7419 "y", b, 7420 "z", c); 7421 } 7422 assert(xyz_abc("_x").equal("_a")); 7423 assert(xyz_abc(".y.").equal(".b.")); 7424 assert(xyz_abc("z").equal("c")); 7425 assert(xyz_abc("w").equal("w")); 7426 } 7427 7428 // test with narrow strings (auto-decoding) and subranges 7429 @safe pure unittest 7430 { 7431 import std.algorithm.comparison : equal; 7432 7433 assert("äöü€".substitute("ä", "b", "ü", "u").equal("böu€")); 7434 assert("äöü€".substitute!("ä", "b", "ü", "u").equal("böu€")); 7435 assert("ä...öü€".substitute("ä", "b", "ü", "u").equal("b...öu€")); 7436 7437 auto expected = "emoticons😄😅.😇😈Rock"; 7438 assert("emoticons😄😅😆😇😈rock" 7439 .substitute("r", "R", "😆", ".").equal(expected)); 7440 assert("emoticons😄😅😆😇😈rock" 7441 .substitute!("r", "R", "😆", ".").equal(expected)); 7442 } 7443 7444 // test with narrow strings (auto-decoding) and single elements 7445 @safe pure unittest 7446 { 7447 import std.algorithm.comparison : equal; 7448 7449 assert("äöü€".substitute('ä', 'b', 'ü', 'u').equal("böu€")); 7450 assert("äöü€".substitute!('ä', 'b', 'ü', 'u').equal("böu€")); 7451 7452 auto expected = "emoticons😄😅.😇😈Rock"; 7453 assert("emoticons😄😅😆😇😈rock" 7454 .substitute('r', 'R', '😆', '.').equal(expected)); 7455 assert("emoticons😄😅😆😇😈rock" 7456 .substitute!('r', 'R', '😆', '.').equal(expected)); 7457 } 7458 7459 // test auto-decoding {n,w,d} strings X {n,w,d} strings 7460 @safe pure unittest 7461 { 7462 import std.algorithm.comparison : equal; 7463 7464 assert("ääöü€".substitute("ä", "b", "ü", "u").equal("bböu€")); 7465 assert("ääöü€".substitute("ä"w, "b"w, "ü"w, "u"w).equal("bböu€")); 7466 assert("ääöü€".substitute("ä"d, "b"d, "ü"d, "u"d).equal("bböu€")); 7467 7468 assert("ääöü€"w.substitute("ä", "b", "ü", "u").equal("bböu€")); 7469 assert("ääöü€"w.substitute("ä"w, "b"w, "ü"w, "u"w).equal("bböu€")); 7470 assert("ääöü€"w.substitute("ä"d, "b"d, "ü"d, "u"d).equal("bböu€")); 7471 7472 assert("ääöü€"d.substitute("ä", "b", "ü", "u").equal("bböu€")); 7473 assert("ääöü€"d.substitute("ä"w, "b"w, "ü"w, "u"w).equal("bböu€")); 7474 assert("ääöü€"d.substitute("ä"d, "b"d, "ü"d, "u"d).equal("bböu€")); 7475 7476 // auto-decoding is done before by a different range 7477 assert("ääöü€".filter!(a => true).substitute("ä", "b", "ü", "u").equal("bböu€")); 7478 assert("ääöü€".filter!(a => true).substitute("ä"w, "b"w, "ü"w, "u"w).equal("bböu€")); 7479 assert("ääöü€".filter!(a => true).substitute("ä"d, "b"d, "ü"d, "u"d).equal("bböu€")); 7480 } 7481 7482 // test repeated replacement 7483 @safe pure nothrow unittest 7484 { 7485 import std.algorithm.comparison : equal; 7486 7487 assert([1, 2, 3, 1, 1, 2].substitute(1, 0).equal([0, 2, 3, 0, 0, 2])); 7488 assert([1, 2, 3, 1, 1, 2].substitute!(1, 0).equal([0, 2, 3, 0, 0, 2])); 7489 assert([1, 2, 3, 1, 1, 2].substitute(1, 2, 2, 9).equal([2, 9, 3, 2, 2, 9])); 7490 } 7491 7492 // test @nogc for single element replacements 7493 @safe @nogc unittest 7494 { 7495 import std.algorithm.comparison : equal; 7496 7497 static immutable arr = [1, 2, 3, 1, 1, 2]; 7498 static immutable expected = [0, 2, 3, 0, 0, 2]; 7499 7500 assert(arr.substitute!(1, 0).equal(expected)); 7501 assert(arr.substitute(1, 0).equal(expected)); 7502 } 7503 7504 // test different range types 7505 @safe pure nothrow unittest 7506 { 7507 import std.algorithm.comparison : equal; 7508 import std.internal.test.dummyrange : AllDummyRanges; 7509 7510 static foreach (DummyType; AllDummyRanges) 7511 {{ 7512 DummyType dummyRange; 7513 7514 // single substitution 7515 dummyRange.substitute (2, 22).equal([1, 22, 3, 4, 5, 6, 7, 8, 9, 10]); 7516 dummyRange.substitute!(2, 22).equal([1, 22, 3, 4, 5, 6, 7, 8, 9, 10]); 7517 7518 // multiple substitution 7519 dummyRange.substitute (2, 22, 5, 55, 7, 77).equal([1, 22, 3, 4, 55, 6, 77, 8, 9, 10]); 7520 dummyRange.substitute!(2, 22, 5, 55, 7, 77).equal([1, 22, 3, 4, 55, 6, 77, 8, 9, 10]); 7521 }} 7522 } 7523 7524 // https://issues.dlang.org/show_bug.cgi?id=19207 7525 @safe pure nothrow unittest 7526 { 7527 import std.algorithm.comparison : equal; 7528 assert([1, 2, 3, 4].substitute([1], [7]).equal([7, 2, 3, 4])); 7529 assert([1, 2, 3, 4].substitute([2], [7]).equal([1, 7, 3, 4])); 7530 assert([1, 2, 3, 4].substitute([4], [7]).equal([1, 2, 3, 7])); 7531 assert([1, 2, 3, 4].substitute([2, 3], [7]).equal([1, 7, 4])); 7532 assert([1, 2, 3, 4].substitute([3, 4], [7, 8]).equal([1, 2, 7, 8])); 7533 } 7534 7535 // tests recognizing empty base ranges 7536 nothrow pure @safe unittest 7537 { 7538 import std.utf : byCodeUnit; 7539 import std.algorithm.comparison : equal; 7540 7541 assert("".byCodeUnit.substitute('4', 'A').empty); 7542 assert("".byCodeUnit.substitute('0', 'O', '5', 'S', '1', 'l').empty); 7543 assert("".byCodeUnit.substitute("PKM".byCodeUnit, "PoKeMon".byCodeUnit).empty); 7544 assert("".byCodeUnit.substitute 7545 ( "ding".byCodeUnit, 7546 "dong".byCodeUnit, 7547 "click".byCodeUnit, 7548 "clack".byCodeUnit, 7549 "ping".byCodeUnit, 7550 "latency".byCodeUnit 7551 ).empty); 7552 } 7553 7554 // sum 7555 /** 7556 Sums elements of `r`, which must be a finite 7557 $(REF_ALTTEXT input range, isInputRange, std,range,primitives). Although 7558 conceptually `sum(r)` is equivalent to $(LREF fold)!((a, b) => a + 7559 b)(r, 0), `sum` uses specialized algorithms to maximize accuracy, 7560 as follows. 7561 7562 $(UL 7563 $(LI If $(REF ElementType, std,range,primitives)!R is a floating-point 7564 type and `R` is a 7565 $(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives) with 7566 length and slicing, then `sum` uses the 7567 $(HTTP en.wikipedia.org/wiki/Pairwise_summation, pairwise summation) 7568 algorithm.) 7569 $(LI If `ElementType!R` is a floating-point type and `R` is a 7570 finite input range (but not a random-access range with slicing), then 7571 `sum` uses the $(HTTP en.wikipedia.org/wiki/Kahan_summation, 7572 Kahan summation) algorithm.) 7573 $(LI In all other cases, a simple element by element addition is done.) 7574 ) 7575 7576 For floating point inputs, calculations are made in 7577 $(DDLINK spec/type, Types, `real`) 7578 precision for `real` inputs and in `double` precision otherwise 7579 (Note this is a special case that deviates from `fold`'s behavior, 7580 which would have kept `float` precision for a `float` range). 7581 For all other types, the calculations are done in the same type obtained 7582 from from adding two elements of the range, which may be a different 7583 type from the elements themselves (for example, in case of 7584 $(DDSUBLINK spec/type,integer-promotions, integral promotion)). 7585 7586 A seed may be passed to `sum`. Not only will this seed be used as an initial 7587 value, but its type will override all the above, and determine the algorithm 7588 and precision used for summation. If a seed is not passed, one is created with 7589 the value of `typeof(r.front + r.front)(0)`, or `typeof(r.front + r.front).zero` 7590 if no constructor exists that takes an int. 7591 7592 Note that these specialized summing algorithms execute more primitive operations 7593 than vanilla summation. Therefore, if in certain cases maximum speed is required 7594 at expense of precision, one can use `fold!((a, b) => a + b)(r, 0)`, which 7595 is not specialized for summation. 7596 7597 Params: 7598 seed = the initial value of the summation 7599 r = a finite input range 7600 7601 Returns: 7602 The sum of all the elements in the range r. 7603 */ 7604 auto sum(R)(R r) 7605 if (isInputRange!R && !isInfinite!R && is(typeof(r.front + r.front))) 7606 { 7607 alias E = Unqual!(ElementType!R); 7608 static if (isFloatingPoint!E) 7609 alias Seed = typeof(E.init + 0.0); //biggest of double/real 7610 else 7611 alias Seed = typeof(r.front + r.front); 7612 static if (is(typeof(Unqual!Seed(0)))) 7613 enum seedValue = Unqual!Seed(0); 7614 else static if (is(typeof({ Unqual!Seed tmp = Seed.zero; }))) 7615 enum Unqual!Seed seedValue = Seed.zero; 7616 else 7617 static assert(false, 7618 "Could not initiate an initial value for " ~ (Unqual!Seed).stringof 7619 ~ ". Please supply an initial value manually."); 7620 return sum(r, seedValue); 7621 } 7622 /// ditto 7623 auto sum(R, E)(R r, E seed) 7624 if (isInputRange!R && !isInfinite!R && is(typeof(seed = seed + r.front))) 7625 { 7626 static if (isFloatingPoint!E) 7627 { 7628 static if (hasLength!R && hasSlicing!R) 7629 { 7630 if (r.empty) return seed; 7631 return seed + sumPairwise!E(r); 7632 } 7633 else 7634 return sumKahan!E(seed, r); 7635 } 7636 else 7637 { 7638 return reduce!"a + b"(seed, r); 7639 } 7640 } 7641 7642 /// Ditto 7643 @safe pure nothrow unittest 7644 { 7645 import std.range; 7646 7647 //simple integral sumation 7648 assert(sum([ 1, 2, 3, 4]) == 10); 7649 7650 //with integral promotion 7651 assert(sum([false, true, true, false, true]) == 3); 7652 assert(sum(ubyte.max.repeat(100)) == 25500); 7653 7654 //The result may overflow 7655 assert(uint.max.repeat(3).sum() == 4294967293U ); 7656 //But a seed can be used to change the sumation primitive 7657 assert(uint.max.repeat(3).sum(ulong.init) == 12884901885UL); 7658 7659 //Floating point sumation 7660 assert(sum([1.0, 2.0, 3.0, 4.0]) == 10); 7661 7662 //Floating point operations have double precision minimum 7663 static assert(is(typeof(sum([1F, 2F, 3F, 4F])) == double)); 7664 assert(sum([1F, 2, 3, 4]) == 10); 7665 7666 //Force pair-wise floating point sumation on large integers 7667 import std.math.operations : isClose; 7668 assert(iota(ulong.max / 2, ulong.max / 2 + 4096).sum(0.0) 7669 .isClose((ulong.max / 2) * 4096.0 + 4096^^2 / 2)); 7670 } 7671 7672 // Pairwise summation http://en.wikipedia.org/wiki/Pairwise_summation 7673 private auto sumPairwise(F, R)(R data) 7674 if (isInputRange!R && !isInfinite!R) 7675 { 7676 import core.bitop : bsf; 7677 // Works for r with at least length < 2^^(64 + log2(16)), in keeping with the use of size_t 7678 // elsewhere in std.algorithm and std.range on 64 bit platforms. The 16 in log2(16) comes 7679 // from the manual unrolling in sumPairWise16 7680 F[64] store = void; 7681 size_t idx = 0; 7682 7683 void collapseStore(T)(T k) 7684 { 7685 auto lastToKeep = idx - cast(uint) bsf(k+1); 7686 while (idx > lastToKeep) 7687 { 7688 store[idx - 1] += store[idx]; 7689 --idx; 7690 } 7691 } 7692 7693 static if (hasLength!R) 7694 { 7695 foreach (k; 0 .. data.length / 16) 7696 { 7697 static if (isRandomAccessRange!R && hasSlicing!R) 7698 { 7699 store[idx] = sumPairwise16!F(data); 7700 data = data[16 .. data.length]; 7701 } 7702 else store[idx] = sumPairwiseN!(16, false, F)(data); 7703 7704 collapseStore(k); 7705 ++idx; 7706 } 7707 7708 size_t i = 0; 7709 foreach (el; data) 7710 { 7711 store[idx] = el; 7712 collapseStore(i); 7713 ++idx; 7714 ++i; 7715 } 7716 } 7717 else 7718 { 7719 size_t k = 0; 7720 while (!data.empty) 7721 { 7722 store[idx] = sumPairwiseN!(16, true, F)(data); 7723 collapseStore(k); 7724 ++idx; 7725 ++k; 7726 } 7727 } 7728 7729 F s = store[idx - 1]; 7730 foreach_reverse (j; 0 .. idx - 1) 7731 s += store[j]; 7732 7733 return s; 7734 } 7735 7736 private auto sumPairwise16(F, R)(R r) 7737 if (isRandomAccessRange!R) 7738 { 7739 return (((cast(F) r[ 0] + r[ 1]) + (cast(F) r[ 2] + r[ 3])) 7740 + ((cast(F) r[ 4] + r[ 5]) + (cast(F) r[ 6] + r[ 7]))) 7741 + (((cast(F) r[ 8] + r[ 9]) + (cast(F) r[10] + r[11])) 7742 + ((cast(F) r[12] + r[13]) + (cast(F) r[14] + r[15]))); 7743 } 7744 7745 private auto sumPair(bool needEmptyChecks, F, R)(ref R r) 7746 if (isForwardRange!R && !isRandomAccessRange!R) 7747 { 7748 static if (needEmptyChecks) if (r.empty) return F(0); 7749 F s0 = r.front; 7750 r.popFront(); 7751 static if (needEmptyChecks) if (r.empty) return s0; 7752 s0 += r.front; 7753 r.popFront(); 7754 return s0; 7755 } 7756 7757 private auto sumPairwiseN(size_t N, bool needEmptyChecks, F, R)(ref R r) 7758 if (isForwardRange!R && !isRandomAccessRange!R) 7759 { 7760 import std.math.traits : isPowerOf2; 7761 static assert(isPowerOf2(N), "N must be a power of 2"); 7762 static if (N == 2) return sumPair!(needEmptyChecks, F)(r); 7763 else return sumPairwiseN!(N/2, needEmptyChecks, F)(r) 7764 + sumPairwiseN!(N/2, needEmptyChecks, F)(r); 7765 } 7766 7767 // Kahan algo http://en.wikipedia.org/wiki/Kahan_summation_algorithm 7768 private auto sumKahan(Result, R)(Result result, R r) 7769 { 7770 static assert(isFloatingPoint!Result && isMutable!Result, "The type of" 7771 ~ " Result must be a mutable floating point, not " 7772 ~ Result.stringof); 7773 Result c = 0; 7774 for (; !r.empty; r.popFront()) 7775 { 7776 immutable y = r.front - c; 7777 immutable t = result + y; 7778 c = (t - result) - y; 7779 result = t; 7780 } 7781 return result; 7782 } 7783 7784 @safe pure nothrow unittest 7785 { 7786 static assert(is(typeof(sum([cast( byte) 1])) == int)); 7787 static assert(is(typeof(sum([cast(ubyte) 1])) == int)); 7788 static assert(is(typeof(sum([ 1, 2, 3, 4])) == int)); 7789 static assert(is(typeof(sum([ 1U, 2U, 3U, 4U])) == uint)); 7790 static assert(is(typeof(sum([ 1L, 2L, 3L, 4L])) == long)); 7791 static assert(is(typeof(sum([1UL, 2UL, 3UL, 4UL])) == ulong)); 7792 7793 int[] empty; 7794 assert(sum(empty) == 0); 7795 assert(sum([42]) == 42); 7796 assert(sum([42, 43]) == 42 + 43); 7797 assert(sum([42, 43, 44]) == 42 + 43 + 44); 7798 assert(sum([42, 43, 44, 45]) == 42 + 43 + 44 + 45); 7799 } 7800 7801 @safe pure nothrow unittest 7802 { 7803 static assert(is(typeof(sum([1.0, 2.0, 3.0, 4.0])) == double)); 7804 static assert(is(typeof(sum([ 1F, 2F, 3F, 4F])) == double)); 7805 const(float[]) a = [1F, 2F, 3F, 4F]; 7806 assert(sum(a) == 10F); 7807 static assert(is(typeof(sum(a)) == double)); 7808 7809 double[] empty; 7810 assert(sum(empty) == 0); 7811 assert(sum([42.]) == 42); 7812 assert(sum([42., 43.]) == 42 + 43); 7813 assert(sum([42., 43., 44.]) == 42 + 43 + 44); 7814 assert(sum([42., 43., 44., 45.5]) == 42 + 43 + 44 + 45.5); 7815 } 7816 7817 @safe pure nothrow unittest 7818 { 7819 import std.container; 7820 static assert(is(typeof(sum(SList!float()[])) == double)); 7821 static assert(is(typeof(sum(SList!double()[])) == double)); 7822 static assert(is(typeof(sum(SList!real()[])) == real)); 7823 7824 assert(sum(SList!double()[]) == 0); 7825 assert(sum(SList!double(1)[]) == 1); 7826 assert(sum(SList!double(1, 2)[]) == 1 + 2); 7827 assert(sum(SList!double(1, 2, 3)[]) == 1 + 2 + 3); 7828 assert(sum(SList!double(1, 2, 3, 4)[]) == 10); 7829 } 7830 7831 // https://issues.dlang.org/show_bug.cgi?id=12434 7832 @safe pure nothrow unittest 7833 { 7834 immutable a = [10, 20]; 7835 auto s1 = sum(a); 7836 assert(s1 == 30); 7837 auto s2 = a.map!(x => x).sum; 7838 assert(s2 == 30); 7839 } 7840 7841 @system unittest 7842 { 7843 import std.bigint; 7844 import std.range; 7845 7846 immutable BigInt[] a = BigInt("1_000_000_000_000_000_000").repeat(10).array(); 7847 immutable ulong[] b = (ulong.max/2).repeat(10).array(); 7848 auto sa = a.sum(); 7849 auto sb = b.sum(BigInt(0)); //reduce ulongs into bigint 7850 assert(sa == BigInt("10_000_000_000_000_000_000")); 7851 assert(sb == (BigInt(ulong.max/2) * 10)); 7852 } 7853 7854 @safe pure nothrow @nogc unittest 7855 { 7856 import std.range; 7857 foreach (n; iota(50)) 7858 assert(repeat(1.0, n).sum == n); 7859 } 7860 7861 // Issue 19525 7862 @safe unittest 7863 { 7864 import std.datetime : Duration, minutes; 7865 assert([1.minutes].sum() == 1.minutes); 7866 } 7867 7868 /** 7869 Finds the mean (colloquially known as the average) of a range. 7870 7871 For built-in numerical types, accurate Knuth & Welford mean calculation 7872 is used. For user-defined types, element by element summation is used. 7873 Additionally an extra parameter `seed` is needed in order to correctly 7874 seed the summation with the equivalent to `0`. 7875 7876 The first overload of this function will return `T.init` if the range 7877 is empty. However, the second overload will return `seed` on empty ranges. 7878 7879 This function is $(BIGOH r.length). 7880 7881 Params: 7882 T = The type of the return value. 7883 r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 7884 seed = For user defined types. Should be equivalent to `0`. 7885 7886 Returns: 7887 The mean of `r` when `r` is non-empty. 7888 */ 7889 T mean(T = double, R)(R r) 7890 if (isInputRange!R && 7891 isNumeric!(ElementType!R) && 7892 !isInfinite!R) 7893 { 7894 if (r.empty) 7895 return T.init; 7896 7897 Unqual!T meanRes = 0; 7898 size_t i = 1; 7899 7900 // Knuth & Welford mean calculation 7901 // division per element is slower, but more accurate 7902 for (; !r.empty; r.popFront()) 7903 { 7904 T delta = r.front - meanRes; 7905 meanRes += delta / i++; 7906 } 7907 7908 return meanRes; 7909 } 7910 7911 /// ditto 7912 auto mean(R, T)(R r, T seed) 7913 if (isInputRange!R && 7914 !isNumeric!(ElementType!R) && 7915 is(typeof(r.front + seed)) && 7916 is(typeof(r.front / size_t(1))) && 7917 !isInfinite!R) 7918 { 7919 import std.algorithm.iteration : sum, reduce; 7920 7921 // per item division vis-a-vis the previous overload is too 7922 // inaccurate for integer division, which the user defined 7923 // types might be representing 7924 static if (hasLength!R) 7925 { 7926 if (r.length == 0) 7927 return seed; 7928 7929 return sum(r, seed) / r.length; 7930 } 7931 else 7932 { 7933 import std.typecons : tuple; 7934 7935 if (r.empty) 7936 return seed; 7937 7938 auto pair = reduce!((a, b) => tuple(a[0] + 1, a[1] + b)) 7939 (tuple(size_t(0), seed), r); 7940 return pair[1] / pair[0]; 7941 } 7942 } 7943 7944 /// 7945 @safe @nogc pure nothrow unittest 7946 { 7947 import std.math.operations : isClose; 7948 import std.math.traits : isNaN; 7949 7950 static immutable arr1 = [1, 2, 3]; 7951 static immutable arr2 = [1.5, 2.5, 12.5]; 7952 7953 assert(arr1.mean.isClose(2)); 7954 assert(arr2.mean.isClose(5.5)); 7955 7956 assert(arr1[0 .. 0].mean.isNaN); 7957 } 7958 7959 @safe pure nothrow unittest 7960 { 7961 import std.internal.test.dummyrange : ReferenceInputRange; 7962 import std.math.operations : isClose; 7963 7964 auto r1 = new ReferenceInputRange!int([1, 2, 3]); 7965 assert(r1.mean.isClose(2)); 7966 7967 auto r2 = new ReferenceInputRange!double([1.5, 2.5, 12.5]); 7968 assert(r2.mean.isClose(5.5)); 7969 } 7970 7971 // Test user defined types 7972 @system pure unittest 7973 { 7974 import std.bigint : BigInt; 7975 import std.internal.test.dummyrange : ReferenceInputRange; 7976 import std.math.operations : isClose; 7977 7978 auto bigint_arr = [BigInt("1"), BigInt("2"), BigInt("3"), BigInt("6")]; 7979 auto bigint_arr2 = new ReferenceInputRange!BigInt([ 7980 BigInt("1"), BigInt("2"), BigInt("3"), BigInt("6") 7981 ]); 7982 assert(bigint_arr.mean(BigInt(0)) == BigInt("3")); 7983 assert(bigint_arr2.mean(BigInt(0)) == BigInt("3")); 7984 7985 BigInt[] bigint_arr3 = []; 7986 assert(bigint_arr3.mean(BigInt(0)) == BigInt(0)); 7987 7988 struct MyFancyDouble 7989 { 7990 double v; 7991 alias v this; 7992 } 7993 7994 // both overloads 7995 auto d_arr = [MyFancyDouble(10), MyFancyDouble(15), MyFancyDouble(30)]; 7996 assert(mean!(double)(cast(double[]) d_arr).isClose(18.33333333)); 7997 assert(mean(d_arr, MyFancyDouble(0)).isClose(18.33333333)); 7998 } 7999 8000 // uniq 8001 /** 8002 Lazily iterates unique consecutive elements of the given range, which is 8003 assumed to be sorted (functionality akin to the 8004 $(HTTP wikipedia.org/wiki/_Uniq, _uniq) system 8005 utility). Equivalence of elements is assessed by using the predicate 8006 `pred`, by default `"a == b"`. The predicate is passed to 8007 $(REF binaryFun, std,functional), and can either accept a string, or any callable 8008 that can be executed via `pred(element, element)`. If the given range is 8009 bidirectional, `uniq` also yields a 8010 $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives). 8011 8012 Params: 8013 pred = Predicate for determining equivalence between range elements. 8014 r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of 8015 elements to filter. 8016 8017 Returns: 8018 An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of 8019 consecutively unique elements in the original range. If `r` is also a 8020 forward range or bidirectional range, the returned range will be likewise. 8021 */ 8022 auto uniq(alias pred = "a == b", Range)(Range r) 8023 if (isInputRange!Range && is(typeof(binaryFun!pred(r.front, r.front)) == bool)) 8024 { 8025 return UniqResult!(binaryFun!pred, Range)(r); 8026 } 8027 8028 /// 8029 @safe unittest 8030 { 8031 import std.algorithm.comparison : equal; 8032 import std.algorithm.mutation : copy; 8033 8034 int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; 8035 assert(equal(uniq(arr), [ 1, 2, 3, 4, 5 ][])); 8036 8037 // Filter duplicates in-place using copy 8038 arr.length -= arr.uniq().copy(arr).length; 8039 assert(arr == [ 1, 2, 3, 4, 5 ]); 8040 8041 // Note that uniqueness is only determined consecutively; duplicated 8042 // elements separated by an intervening different element will not be 8043 // eliminated: 8044 assert(equal(uniq([ 1, 1, 2, 1, 1, 3, 1]), [1, 2, 1, 3, 1])); 8045 } 8046 8047 private struct UniqResult(alias pred, Range) 8048 { 8049 Range _input; 8050 8051 this(Range input) 8052 { 8053 _input = input; 8054 } 8055 8056 auto opSlice() 8057 { 8058 return this; 8059 } 8060 8061 void popFront() 8062 { 8063 assert(!empty, "Attempting to popFront an empty uniq."); 8064 auto last = _input.front; 8065 do 8066 { 8067 _input.popFront(); 8068 } 8069 while (!_input.empty && pred(last, _input.front)); 8070 } 8071 8072 @property ElementType!Range front() 8073 { 8074 assert(!empty, "Attempting to fetch the front of an empty uniq."); 8075 return _input.front; 8076 } 8077 8078 static if (isBidirectionalRange!Range) 8079 { 8080 void popBack() 8081 { 8082 assert(!empty, "Attempting to popBack an empty uniq."); 8083 auto last = _input.back; 8084 do 8085 { 8086 _input.popBack(); 8087 } 8088 while (!_input.empty && pred(last, _input.back)); 8089 } 8090 8091 @property ElementType!Range back() 8092 { 8093 assert(!empty, "Attempting to fetch the back of an empty uniq."); 8094 return _input.back; 8095 } 8096 } 8097 8098 static if (isInfinite!Range) 8099 { 8100 enum bool empty = false; // Propagate infiniteness. 8101 } 8102 else 8103 { 8104 @property bool empty() { return _input.empty; } 8105 } 8106 8107 static if (isForwardRange!Range) 8108 { 8109 @property typeof(this) save() { 8110 return typeof(this)(_input.save); 8111 } 8112 } 8113 } 8114 8115 @safe unittest 8116 { 8117 import std.algorithm.comparison : equal; 8118 import std.internal.test.dummyrange; 8119 import std.range; 8120 8121 int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; 8122 auto r = uniq(arr); 8123 static assert(isForwardRange!(typeof(r))); 8124 8125 assert(equal(r, [ 1, 2, 3, 4, 5 ][])); 8126 assert(equal(retro(r), retro([ 1, 2, 3, 4, 5 ][]))); 8127 8128 foreach (DummyType; AllDummyRanges) 8129 { 8130 DummyType d; 8131 auto u = uniq(d); 8132 assert(equal(u, [1,2,3,4,5,6,7,8,9,10])); 8133 8134 static assert(d.rt == RangeType.Input || isForwardRange!(typeof(u))); 8135 8136 static if (d.rt >= RangeType.Bidirectional) 8137 { 8138 assert(equal(retro(u), [10,9,8,7,6,5,4,3,2,1])); 8139 } 8140 } 8141 } 8142 8143 // https://issues.dlang.org/show_bug.cgi?id=17264 8144 @safe unittest 8145 { 8146 import std.algorithm.comparison : equal; 8147 8148 const(int)[] var = [0, 1, 1, 2]; 8149 assert(var.uniq.equal([0, 1, 2])); 8150 } 8151 8152 /** 8153 Lazily computes all _permutations of `r` using $(HTTP 8154 en.wikipedia.org/wiki/Heap%27s_algorithm, Heap's algorithm). 8155 8156 Params: 8157 Range = the range type 8158 r = the $(REF_ALTTEXT random access range, isRandomAccessRange, std,range,primitives) 8159 to find the permutations for. 8160 Returns: 8161 A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) 8162 of elements of which are an $(REF indexed, std,range) view into `r`. 8163 8164 See_Also: 8165 $(REF nextPermutation, std,algorithm,sorting). 8166 */ 8167 Permutations!Range permutations(Range)(Range r) 8168 { 8169 static assert(isRandomAccessRange!Range, Range.stringof, 8170 " must be a RandomAccessRange"); 8171 static assert(hasLength!Range, Range.stringof 8172 , " must have a length"); 8173 8174 return typeof(return)(r); 8175 } 8176 8177 /// ditto 8178 struct Permutations(Range) 8179 { 8180 static assert(isRandomAccessRange!Range, Range.stringof, 8181 " must be a RandomAccessRange"); 8182 static assert(hasLength!Range, Range.stringof 8183 , " must have a length"); 8184 8185 private size_t[] _indices, _state; 8186 private Range _r; 8187 private bool _empty; 8188 8189 /// 8190 this(Range r) 8191 { 8192 import std.array : array; 8193 import std.range : iota; 8194 8195 this._r = r; 8196 _state = r.length ? new size_t[r.length-1] : null; 8197 _indices = iota(size_t(r.length)).array; 8198 _empty = r.length == 0; 8199 } 8200 private this(size_t[] indices, size_t[] state, Range r, bool empty_) 8201 { 8202 _indices = indices; 8203 _state = state; 8204 _r = r; 8205 _empty = empty_; 8206 } 8207 /// Returns: `true` if the range is empty, `false` otherwise. 8208 @property bool empty() const pure nothrow @safe @nogc 8209 { 8210 return _empty; 8211 } 8212 8213 /// Returns: the front of the range 8214 @property auto front() 8215 { 8216 import std.range : indexed; 8217 return _r.indexed(_indices); 8218 } 8219 8220 /// 8221 void popFront() 8222 { 8223 void next(int n) 8224 { 8225 import std.algorithm.mutation : swap; 8226 8227 if (n > _indices.length) 8228 { 8229 _empty = true; 8230 return; 8231 } 8232 8233 if (n % 2 == 1) 8234 swap(_indices[0], _indices[n-1]); 8235 else 8236 swap(_indices[_state[n-2]], _indices[n-1]); 8237 8238 if (++_state[n-2] == n) 8239 { 8240 _state[n-2] = 0; 8241 next(n+1); 8242 } 8243 } 8244 8245 next(2); 8246 } 8247 /// Returns: an independent copy of the permutations range. 8248 auto save() 8249 { 8250 return typeof(this)(_indices.dup, _state.dup, _r.save, _empty); 8251 } 8252 } 8253 8254 /// 8255 @safe unittest 8256 { 8257 import std.algorithm.comparison : equal; 8258 import std.range : iota; 8259 assert(equal!equal(iota(3).permutations, 8260 [[0, 1, 2], 8261 [1, 0, 2], 8262 [2, 0, 1], 8263 [0, 2, 1], 8264 [1, 2, 0], 8265 [2, 1, 0]])); 8266 } 8267 8268 @safe unittest 8269 { 8270 import std.algorithm.comparison : equal; 8271 import std.range : ElementType; 8272 import std.array : array; 8273 auto p = [1, 2, 3].permutations; 8274 auto x = p.save.front; 8275 p.popFront; 8276 auto y = p.front; 8277 assert(x != y); 8278 }