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