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