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