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