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