1 #!/usr/bin/env rdmd-dev-module 2 3 /** Extensions to std.range. 4 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 5 */ 6 7 module range_ex; 8 9 import std.range: hasSlicing, isSomeString, isNarrowString, isInfinite, isInputRange, ElementType; 10 import std.traits: hasUnsharedAliasing, hasElaborateDestructor, isScalarType; 11 12 public import slicing; 13 14 enum hasPureCopy(T) = (isScalarType!T || // TODO remove? 15 (!hasUnsharedAliasing!T && 16 !hasElaborateDestructor!T)); 17 18 enum hasStealableElements(R) = (hasPureCopy!(ElementType!R)); // TODO recurse 19 20 /* template hasStealableElements(T...) */ 21 /* { */ 22 /* import std.range: ElementType; */ 23 /* import std.typecons : Rebindable; */ 24 25 /* static if (is(ElementType!T)) */ 26 /* { */ 27 /* enum hasStealableElements = true; */ 28 /* } */ 29 /* else static if (is(T[0] R: Rebindable!R)) */ 30 /* { */ 31 /* enum hasStealableElements = hasStealableElements!R; */ 32 /* } */ 33 /* else */ 34 /* { */ 35 /* template unsharedDelegate(T) */ 36 /* { */ 37 /* enum bool unsharedDelegate = isDelegate!T */ 38 /* && !is(T == shared) */ 39 /* && !is(T == shared) */ 40 /* && !is(T == immutable) */ 41 /* && !is(FunctionTypeOf!T == shared) */ 42 /* && !is(FunctionTypeOf!T == immutable); */ 43 /* } */ 44 45 /* enum hasStealableElements = */ 46 /* hasRawUnsharedAliasing!(T[0]) || */ 47 /* anySatisfy!(unsharedDelegate, RepresentationTypeTuple!(T[0])) || */ 48 /* hasUnsharedObjects!(T[0]) || */ 49 /* hasStealableElements!(T[1..$]); */ 50 /* } */ 51 /* } */ 52 53 @safe @nogc pure nothrow unittest 54 { 55 static assert(hasStealableElements!(int[])); 56 57 import std.stdio: File; 58 alias BL = File.ByLine!(char, char); 59 static assert(!hasStealableElements!BL); 60 } 61 62 /** Steal front from $(D r) destructively and return it. 63 See also: http://forum.dlang.org/thread/jkbhlezbcrufowxtthmy@forum.dlang.org#post-konhvblwbmpdrbeqhyuv:40forum.dlang.org 64 See also: http://forum.dlang.org/thread/onibkzepudfisxtrigsi@forum.dlang.org#post-dafmzroxvaeejyxrkbon:40forum.dlang.org 65 */ 66 auto stealFront(R)(ref R r) 67 if (isInputRange!R && 68 hasStealableElements!R) 69 { 70 import std.range: moveFront, popFront; 71 /* scope(success) r.popFront; */ 72 /* return r.moveFront; */ 73 auto e = r.moveFront; 74 r.popFront; 75 return e; 76 } 77 alias pullFront = stealFront; 78 alias takeFront = stealFront; 79 80 @safe pure nothrow unittest 81 { 82 auto x = [11, 22]; 83 assert(x.stealFront == 11); assert(x == [22]); 84 assert(x.stealFront == 22); assert(x == []); 85 } 86 87 @safe pure nothrow unittest 88 { 89 auto x = ["a", "b"]; 90 assert(x.stealFront == "a"); assert(x == ["b"]); 91 } 92 93 @safe pure nothrow unittest 94 { 95 struct V { int x, y; } 96 auto x = [V(11, 12), 97 V(21, 22)]; 98 assert(x.stealFront == V(11, 12)); assert(x == [V(21, 22)]); 99 assert(x.stealFront == V(21, 22)); assert(x == []); 100 } 101 102 /** Steal back from $(D r) destructively and return it. 103 See also: http://forum.dlang.org/thread/jkbhlezbcrufowxtthmy@forum.dlang.org#post-konhvblwbmpdrbeqhyuv:40forum.dlang.org 104 See also: http://forum.dlang.org/thread/onibkzepudfisxtrigsi@forum.dlang.org#post-dafmzroxvaeejyxrkbon:40forum.dlang.org 105 */ 106 auto stealBack(R)(ref R r) 107 if (isInputRange!R && 108 hasStealableElements!R) 109 { 110 import std.range: moveBack, popBack; 111 /* scope(success) r.popBack; */ 112 /* return r.moveBack; */ 113 auto e = r.moveBack; 114 r.popBack; 115 return e; 116 } 117 alias pullBack = stealBack; 118 alias takeBack = stealBack; 119 120 @safe pure nothrow unittest 121 { 122 auto x = [11, 22]; 123 assert(x.stealBack == 22); assert(x == [11]); 124 assert(x.stealBack == 11); assert(x == []); 125 } 126 127 @safe pure nothrow unittest 128 { 129 auto x = ["a", "b"]; 130 assert(x.stealBack == "b"); assert(x == ["a"]); 131 } 132 133 @safe pure nothrow unittest 134 { 135 struct V { int x, y; } 136 auto x = [V(11, 12), 137 V(21, 22)]; 138 assert(x.stealBack == V(21, 22)); assert(x == [V(11, 12)]); 139 assert(x.stealBack == V(11, 12)); assert(x == []); 140 } 141 142 /** Sliding Splitter. 143 See also: http://forum.dlang.org/thread/dndicafxfubzmndehzux@forum.dlang.org 144 See also: http://forum.dlang.org/thread/uzrbmjonrkixojzflbig@forum.dlang.org#epost-viwkavbmwouiquoqwntm:40forum.dlang.org 145 146 TODO Use size_t for _lower and _upper instead and reserve _upper = 147 size_t.max for emptyness? 148 149 TODO Should lower and upper operate on code units instead of code 150 point if isNarrowString!Range. ? 151 152 TODO generalize with stride 153 */ 154 struct SlidingSplitter(Range) 155 if (isSomeString!Range || 156 (hasSlicing!Range && 157 !isInfinite!Range)) 158 { 159 import std.range: isForwardRange; 160 import std.typecons: Unqual, Tuple, tuple; 161 alias R = Unqual!Range; 162 163 this(R)(R data, size_t lower = 0) 164 in { assert(lower <= data.length); } 165 body 166 { 167 _data = data; 168 static if (hasSlicing!Range) // TODO should we use isSomeString here instead? 169 { 170 _lower = lower; 171 _upper = data.length; 172 } 173 else 174 { 175 while (lower) 176 { 177 popFront; 178 --lower; 179 } 180 } 181 _upper = data.length; 182 } 183 184 this(R)(R data, size_t lower, size_t upper) 185 in { assert(lower <= upper + 1 || // the extra + 1 makes empty initialization (lower + 1 == upper) possible in for example opSlice below 186 ((lower <= data.length) && 187 (upper <= data.length))); } 188 body 189 { 190 _data = data; 191 _lower = lower; 192 _upper = upper; 193 } 194 195 @property Tuple!(R, R) front() 196 { 197 return typeof(return)(_data[0 .. _lower], 198 _data[_lower .. $]); 199 } 200 201 void popFront() 202 { 203 static if (isNarrowString!R) 204 { 205 if (_lower < _upper) 206 { 207 import std.utf: stride; 208 _lower += stride(_data, _lower); 209 } 210 else // when we can't decode beyond 211 { 212 ++_lower; // so just indicate we're beyond back 213 } 214 } 215 else 216 { 217 ++_lower; 218 } 219 } 220 221 static if (!isInfinite!R) 222 { 223 @property Tuple!(R, R) back() 224 { 225 return typeof(return)(_data[0 .. _upper], 226 _data[_upper .. $]); 227 } 228 void popBack() 229 { 230 static if (isNarrowString!R) 231 { 232 if (_lower < _upper) 233 { 234 import std.utf: strideBack; 235 _upper -= strideBack(_data, _upper); 236 } 237 else // when we can't decode beyond 238 { 239 --_upper; // so just indicate we're beyond front 240 } 241 } 242 else 243 { 244 --_upper; 245 } 246 } 247 } 248 249 static if (isForwardRange!R) 250 { 251 @property auto save() 252 { 253 import std.range: save; 254 return typeof(this)(_data.save, _lower, _upper); 255 } 256 } 257 258 static if (isInfinite!R) 259 { 260 enum bool empty = false; // propagate infiniteness 261 } 262 else 263 { 264 @property bool empty() const 265 { 266 return _upper < _lower; 267 } 268 } 269 270 static if (hasSlicing!R) 271 { 272 Tuple!(R, R) opIndex(size_t i) 273 in { assert(i < length); } 274 body 275 { 276 return typeof(return)(_data[0 .. _lower + i], 277 _data[_lower + i .. _upper]); 278 } 279 280 typeof(this) opSlice(size_t lower, size_t upper) 281 { 282 if (lower == upper) 283 { 284 return slidingSplitter(_data, 285 _upper + 1, // defines empty intialization 286 _upper); 287 } 288 else 289 { 290 return slidingSplitter(_data, 291 _lower + lower, 292 _lower + (upper - 1)); 293 } 294 } 295 296 // TODO Should length be provided if isNarrowString!Range? 297 @property size_t length() const 298 { 299 return _upper - _lower + 1; 300 } 301 } 302 303 private R _data; 304 private ptrdiff_t _lower; 305 private ptrdiff_t _upper; 306 } 307 308 auto slidingSplitter(R)(R data, size_t lower = 0) 309 { 310 return SlidingSplitter!R(data, lower, data.length); 311 } 312 313 auto slidingSplitter(R)(R data, size_t lower, size_t upper) 314 { 315 return SlidingSplitter!R(data, lower, upper); 316 } 317 318 @safe pure nothrow unittest 319 { 320 import std.typecons : tuple; 321 import std.conv: to; 322 323 auto x = [1, 2, 3]; 324 325 import std.range: isInputRange, isForwardRange, isBidirectionalRange, isRandomAccessRange; 326 327 static assert(isInputRange!(SlidingSplitter!(typeof(x)))); 328 static assert(isForwardRange!(SlidingSplitter!(typeof(x)))); 329 // static assert(isBidirectionalRange!(SlidingSplitter!(typeof(x)))); 330 static assert(isRandomAccessRange!(SlidingSplitter!(typeof(x)))); 331 static assert(!isRandomAccessRange!(SlidingSplitter!string)); 332 static assert(!isRandomAccessRange!(SlidingSplitter!wstring)); 333 static assert(isRandomAccessRange!(SlidingSplitter!dstring)); 334 335 auto y = SlidingSplitter!(typeof(x))(x); 336 337 for (size_t i; i < y.length; ++i) 338 { 339 assert(y[i] == tuple(x[0..i], x[i..3])); 340 } 341 342 assert(y.front == tuple([], x)); 343 assert(!y.empty); 344 assert(x.length + 1 == y.length); 345 346 assert(!y.empty); assert(y.front == tuple(x[0 .. 0], x[0 .. 3])); y.popFront; 347 assert(!y.empty); assert(y.front == tuple(x[0 .. 1], x[1 .. 3])); y.popFront; 348 assert(!y.empty); assert(y.front == tuple(x[0 .. 2], x[2 .. 3])); y.popFront; 349 assert(!y.empty); assert(y.front == tuple(x[0 .. 3], x[3 .. 3])); y.popFront; 350 y.popFront; assert(y.empty); 351 } 352 353 @safe pure unittest // forwards 354 { 355 import std.conv: to; 356 357 size_t lower = 2; 358 359 auto name = "Nordlöw"; 360 auto name8 = name.to! string.slidingSplitter(lower); 361 auto name16 = name.to!wstring.slidingSplitter(lower); 362 auto name32 = name.to!dstring.slidingSplitter(lower); 363 364 static assert(!__traits(compiles, { name8.length >= 0; } )); 365 static assert(!__traits(compiles, { name16.length >= 0; } )); 366 assert(name32.length); 367 368 foreach (ch; name8) 369 { 370 foreach (ix; iota!(0, ch.length)) // for each part in split 371 { 372 import std.algorithm: equal; 373 assert(equal(ch[ix], name16.front[ix])); 374 assert(equal(ch[ix], name32.front[ix])); 375 376 } 377 name16.popFront; 378 name32.popFront; 379 } 380 } 381 382 @safe pure unittest // backwards 383 { 384 import std.conv: to; 385 import std.range: retro; 386 387 size_t lower = 2; 388 389 auto name = "Nordlöw"; 390 auto name8 = name.to! string.slidingSplitter(lower).retro; 391 auto name16 = name.to!wstring.slidingSplitter(lower).retro; 392 auto name32 = name.to!dstring.slidingSplitter(lower).retro; 393 394 foreach (ch; name8) 395 { 396 foreach (ix; iota!(0, ch.length)) // for each part in split 397 { 398 import std.algorithm: equal; 399 assert(equal(ch[ix], name16.front[ix])); 400 assert(equal(ch[ix], name32.front[ix])); 401 } 402 name16.popFront; 403 name32.popFront; 404 } 405 } 406 407 @safe pure nothrow unittest // radial 408 { 409 auto x = [1, 2, 3]; 410 import std.range: radial; 411 import std.typecons : tuple; 412 auto s = x.slidingSplitter; 413 auto r = s.radial; 414 assert(!r.empty); assert(r.front == tuple(x[0 .. 1], x[1 .. 3])); r.popFront; 415 assert(!r.empty); assert(r.front == tuple(x[0 .. 2], x[2 .. 3])); r.popFront; 416 assert(!r.empty); assert(r.front == tuple(x[0 .. 0], x[0 .. 3])); r.popFront; 417 assert(!r.empty); assert(r.front == tuple(x[0 .. 3], x[3 .. 3])); r.popFront; 418 assert(r.empty); 419 } 420 421 /** Ring Buffer. 422 See also: http://forum.dlang.org/thread/ltpaqk$2dav$1@digitalmars.com 423 TODO inout 424 */ 425 struct RingBuffer(T) 426 { 427 this(T[] data, size_t length = 0) 428 { 429 enforce(data.length, "empty ring buffer is prohibited"); 430 enforce(length <= data.length, "buffer length shall not be more 431 than buffer capacity"); 432 _data = data; 433 _beginIndex = 0; 434 _length = length; 435 } 436 437 auto opSlice() const 438 { 439 return cycle(_data[0 .. _length]).take(_length); 440 } 441 442 @property auto length() { return _length; } 443 444 private: 445 T[] _data; 446 size_t _beginIndex; 447 size_t _length; 448 } 449 450 /** Static Iota. 451 TODO Add to Phobos. 452 */ 453 template iota(size_t from, size_t to) 454 if (from <= to) 455 { 456 alias iota = siotaImpl!(to-1, from); 457 } 458 private template siotaImpl(size_t to, size_t now) 459 { 460 import std.meta: AliasSeq; 461 static if (now >= to) { alias siotaImpl = AliasSeq!(now); } 462 else { alias siotaImpl = AliasSeq!(now, siotaImpl!(to, now+1)); } 463 } 464 465 /** Same as $(D iota) but with explicit conversion to type $(D T). 466 See also: http://forum.dlang.org/thread/mailman.955.1444358510.22025.digitalmars-d@puremagic.com?page=1 467 */ 468 auto iotaOf(T, B, E, S)(B begin, E end, S step = 1) 469 { 470 import std.range : iota; 471 import std.algorithm.iteration : map; 472 import std.conv : to; 473 return iota(begin, end, step).map!(a => cast(T)a); 474 } 475 476 // @safe pure unittest 477 // { 478 // import std.array : array; 479 // import std.exception : assertThrown; 480 // import std.meta : AliasSeq; 481 // foreach (T; AliasSeq!(ubyte, ushort, uint, ulong)) 482 // { 483 // auto x = iotaOf!T(0, T.max + 1); 484 // import traits_ex : ElementTypeOf; 485 // static assert(is(ElementTypeOf!(x) == T)); 486 // } 487 // } 488 489 auto iotaOfExceptional(T, B, E, S)(B begin, E end, S step = 1) 490 { 491 import std.range : iota; 492 import std.algorithm.iteration : map; 493 import std.conv : to; 494 return iota(begin, end, step).map!(a => a.to!T); 495 } 496 497 // @safe pure unittest 498 // { 499 // import std.array : array; 500 // import std.exception : assertThrown; 501 // import std.conv; 502 // alias T = ubyte; 503 // auto x = iotaOfExceptional!T(0, T.max + 1); 504 // import traits_ex : ElementTypeOf; 505 // static assert(is(ElementTypeOf!() == T)); 506 // assertThrown!ConvOverflowException(iotaOfExceptional!T(0, T.max + 1 + 1).array); 507 // } 508 509 public import std.array: byPair; 510 alias byItem = byPair; // TODO Is this Python-style naming better? 511 512 unittest 513 { 514 string[int] x; 515 x[0] = "a"; 516 import std.algorithm: equal; 517 import std.typecons : tuple; 518 assert(equal(x.byPair, [tuple(0, "a")])); 519 } 520 521 /** Return Array of Key-Value Pairs of Associative Array $(D aa). 522 See also: https://github.com/D-Programming-Language/druntime/pull/574 523 See also: http://forum.dlang.org/thread/dxotcrutrlmszlidufcr@forum.dlang.org?page=2#post-fhkgitmifgnompkqiscd:40forum.dlang.org 524 */ 525 auto pairs(Key, Value)(Value[Key] aa) 526 { 527 import std.typecons: Tuple, tuple; 528 Tuple!(Key,Value)[] arr; 529 arr.reserve(aa.length); 530 foreach (key; aa.byKey) 531 { 532 arr ~= tuple(key, aa[key]); 533 } 534 return arr; 535 } 536 alias items = pairs; // TODO Is this Python-style naming better? 537 538 unittest 539 { 540 string[int] x; 541 x[0] = "a"; 542 import std.typecons : tuple; 543 assert(x.pairs == [tuple(0, "a")]); 544 } 545 546 import std.traits: isInstanceOf; 547 import std.range: SortedRange; 548 import std.meta: allSatisfy, staticMap; 549 import std.typecons: Unqual; 550 551 template CommonElementType(Rs...) 552 { 553 import std.traits: CommonType; 554 import std.range: ElementType; 555 alias CommonElementType = CommonType!(staticMap!(ElementType, Rs)); 556 } 557 enum bool haveCommonElementType(Types...) = !is(CommonElementType!Types == void); 558 unittest 559 { 560 static assert(haveCommonElementType!(bool[], int[])); 561 static assert(!haveCommonElementType!(bool[], int[], string[])); 562 } 563 564 alias isSortedRange(R) = isInstanceOf!(SortedRange, R); // TODO Or use: __traits(isSame, TemplateOf!R, SortedRange) 565 566 /** Merge arguments with comparison. 567 568 TODO is there a compacter (but as efficient) alternative to: 569 570 foreach (i, R; Rs) { case i; ... } 571 572 For higher type-safety we actually need a specific index type that only be 573 either `undefined` or lie within range `[0 .. Rs.length - 1]`. 574 575 See also: http://forum.dlang.org/thread/gvhqbkbkjbohnjawmvkl@forum.dlang.org#post-tnvzgcklcyutevogjsyi:40forum.dlang.org 576 */ 577 auto merge(alias less = "a < b", Rs...)(Rs rs) if (Rs.length > 1 && 578 allSatisfy!(isSortedRange, 579 staticMap!(Unqual, Rs)) && 580 haveCommonElementType!Rs) 581 { 582 import std.range: isForwardRange, isBidirectionalRange, hasLength; 583 584 alias E = CommonElementType!Rs; 585 enum isBidirectional = allSatisfy!(isBidirectionalRange, staticMap!(Unqual, Rs)); 586 587 struct Result 588 { 589 this(Rs source) 590 { 591 this.source = source; 592 this._lastFrontIndex = frontIndex; 593 static if (isBidirectional) 594 this._lastBackIndex = backIndex; 595 } 596 597 @property bool empty() 598 { 599 if (_lastFrontIndex == size_t.max) 600 return true; 601 static if (isBidirectional) 602 return _lastBackIndex == size_t.max; 603 else 604 return false; 605 } 606 607 @property auto ref front() 608 { 609 import std.range: empty, front; 610 final switch (_lastFrontIndex) 611 { 612 foreach (i, _; Rs) 613 { 614 case i: 615 assert(!source[i].empty); 616 return source[i].front; 617 } 618 } 619 assert(0); 620 } 621 622 private size_t frontIndex() 623 { 624 import std.range: empty, front; 625 size_t bestIndex = size_t.max; // indicate undefined 626 E bestElement; 627 foreach (i, _; Rs) 628 { 629 import std.functional: binaryFun; 630 if (!source[i].empty) 631 { 632 if (bestIndex == size_t.max || // either this is the first or 633 binaryFun!less(source[i].front, bestElement)) 634 { 635 bestIndex = i; 636 bestElement = source[i].front; 637 } 638 } 639 } 640 return bestIndex; 641 } 642 643 void popFront() 644 { 645 import std.range: popFront; 646 final switch (_lastFrontIndex) 647 { 648 foreach (i, _; Rs) 649 { 650 case i: 651 source[i].popFront(); 652 break; 653 } 654 } 655 _lastFrontIndex = frontIndex; 656 } 657 658 static if (isBidirectional) 659 { 660 @property auto ref back() 661 { 662 import std.range: empty, back; 663 final switch (_lastBackIndex) 664 { 665 foreach (i, _; Rs) 666 { 667 case i: 668 assert(!source[i].empty); 669 return source[i].back; 670 } 671 } 672 assert(0); 673 } 674 675 private size_t backIndex() 676 { 677 import std.range: empty, back; 678 import std.functional: binaryFun; 679 size_t bestIndex = size_t.max; // indicate undefined 680 E bestElement; 681 foreach (i, _; Rs) 682 { 683 if (!source[i].empty) 684 { 685 if (bestIndex == size_t.max || // either this is the first or 686 binaryFun!less(bestElement, source[i].back)) 687 { 688 bestIndex = i; 689 bestElement = source[i].back; 690 } 691 } 692 } 693 return bestIndex; 694 } 695 696 void popBack() 697 { 698 import std.range: popBack; 699 final switch (_lastBackIndex) 700 { 701 foreach (i, _; Rs) 702 { 703 case i: 704 source[i].popBack(); 705 break; 706 } 707 } 708 _lastBackIndex = backIndex; 709 } 710 } 711 712 static if (allSatisfy!(isForwardRange, staticMap!(Unqual, Rs))) 713 { 714 @property auto save() 715 { 716 import std.range: save; 717 Result result = this; 718 foreach (i, _; Rs) 719 { 720 result.source[i] = result.source[i].save; 721 } 722 return result; 723 } 724 } 725 726 static if (allSatisfy!(hasLength, Rs)) 727 { 728 @property size_t length() 729 { 730 size_t result; 731 foreach (i, _; Rs) 732 { 733 result += source[i].length; 734 } 735 return result; 736 } 737 738 alias opDollar = length; 739 } 740 741 public Rs source; 742 private size_t _lastFrontIndex = size_t.max; 743 static if (isBidirectional) 744 { 745 private size_t _lastBackIndex = size_t.max; 746 } 747 } 748 749 return Result(rs); 750 } 751 752 @safe pure nothrow unittest 753 { 754 import std.algorithm.comparison : equal; 755 import std.range : assumeSorted, retro; 756 757 int[] a = [ 1, 2, 3, 50, 60]; 758 double[] b = [ 10, 20, 30, 40 ]; 759 760 auto r = merge(a.assumeSorted, 761 b.assumeSorted); 762 763 static assert(is(typeof(r.front) == double)); 764 assert(equal(r, [1, 2, 3, 10, 20, 30, 40, 50, 60])); 765 assert(equal(r.retro, [60, 50, 40, 30, 20, 10, 3, 2, 1])); 766 767 r.popFront; 768 assert(equal(r, [2, 3, 10, 20, 30, 40, 50, 60])); 769 r.popBack; 770 assert(equal(r, [2, 3, 10, 20, 30, 40, 50])); 771 r.popFront; 772 assert(equal(r, [3, 10, 20, 30, 40, 50])); 773 r.popBack; 774 assert(equal(r, [3, 10, 20, 30, 40])); 775 r.popFront; 776 assert(equal(r, [10, 20, 30, 40])); 777 r.popBack; 778 assert(equal(r, [10, 20, 30])); 779 r.popFront; 780 assert(equal(r, [20, 30])); 781 r.popBack; 782 assert(equal(r, [20])); 783 r.popFront; 784 assert(r.empty); 785 } 786 787 /// See also: http://forum.dlang.org/post/gkdqakdogqevwzntpgtu@forum.dlang.org 788 template genTypeList(T, size_t n) 789 { 790 import std.meta : AliasSeq; 791 static if (n <= 1) 792 alias genTypeList = T; 793 else 794 alias genTypeList = AliasSeq!(T, genTypeList!(T, n - 1)); 795 } 796 797 /** Return Static Array $(D arr) as a $(D Tuple). 798 See also: http://forum.dlang.org/post/gkdqakdogqevwzntpgtu@forum.dlang.org 799 Check if std.conv.to() support conversion from T[n] to std.typecons.Tuple(T, ...). 800 */ 801 auto asTuple(T, size_t n)(ref T[n] arr) 802 { 803 import std.typecons : Tuple; 804 return Tuple!(genTypeList!(T, n))(arr); 805 } 806 807 /** Return: Adjacent $(D N)-Tuples of $(D r). 808 TODO: Support ref return via $(D zip) for non-const case. 809 TODO Use a ring buffer instead of copy? 810 TODO Add a variant of adjacentTuples that return a static array instead? 811 See also: http://forum.dlang.org/post/gkdqakdogqevwzntpgtu@forum.dlang.org 812 */ 813 auto adjacentTuples(size_t N, R)(R r) 814 if (N >= 2 && 815 isInputRange!R) 816 { 817 struct Range(R) 818 { 819 import std.typecons: Unqual, Tuple; 820 alias E = Unqual!(ElementType!R); 821 enum M = N - 1; // temporary order 822 alias P = E[M]; 823 alias T = Tuple!(genTypeList!(E, N)); // TODO functionize 824 825 this(R r) 826 { 827 this._source = r; 828 foreach (i; 0 .. M) 829 { 830 if (!empty) 831 popFront; 832 } 833 } 834 835 static if (isInfinite!R) 836 { 837 enum bool empty = false; // propagate infiniteness 838 } 839 else 840 { 841 bool empty() @property // TODO can't empty be const when R is a MapResult? 842 { 843 import std.range : empty; 844 return _source.empty; 845 } 846 } 847 848 auto ref front() @property 849 { 850 import std.range : front; 851 T t; 852 t[0 .. M] = _prevs.asTuple; 853 t[M] = _source.front; 854 return t; 855 } 856 857 void popFront() 858 { 859 static if (N >= 3) 860 { 861 // TODO use static foreach to do left-shifting 862 863 // Need $(D copy) because $(D source) and $(D dest) may overlap. 864 // See also: http://dlang.org/arrays.html#overlapping-copying 865 import std.algorithm.mutation : copy; 866 copy(_prevs[1 .. M], _prevs[0 .. M - 1]); 867 } 868 869 import std.range : front, popFront; 870 _prevs[M - 1] = _source.front; 871 _source.popFront; 872 } 873 874 private: 875 P _prevs; 876 R _source; 877 } 878 return Range!R(r); 879 } 880 881 auto adjacentPairs(R)(R r) 882 if (isInputRange!R) 883 { 884 return adjacentTuples!(2, R)(r); 885 } 886 887 auto adjacentTriples(R)(R r) 888 if (isInputRange!R) 889 { 890 return adjacentTuples!(3, R)(r); 891 } 892 893 /// 894 unittest 895 { 896 import std.typecons : t = tuple; 897 import std.algorithm : equal, map; 898 auto x = [1, 2, 3, 4, 5, 6, 7].map!(a => a); // test with ForwardRange 899 auto y = x.adjacentTuples!4; 900 assert(equal(y, [t(1, 2, 3, 4), 901 t(2, 3, 4, 5), 902 t(3, 4, 5, 6), 903 t(4, 5, 6, 7)])); 904 } 905 906 /// 907 unittest 908 { 909 import std.typecons : t = tuple; 910 import std.algorithm : equal; 911 const x = [1, 2, 3, 4]; 912 auto y = x.adjacentPairs; 913 assert(equal(y, [t(1, 2), t(2, 3), t(3, 4)])); 914 } 915 916 /// 917 unittest 918 { 919 import std.typecons : t = tuple; 920 import std.algorithm : equal; 921 auto x = ["1", "2", "3", "4"]; 922 auto y = x.adjacentPairs; 923 assert(equal(y, [t("1", "2"), t("2", "3"), t("3", "4")])); 924 } 925 926 /// 927 unittest 928 { 929 import std.typecons : t = tuple; 930 import std.algorithm : equal; 931 const x = ["1", "2", "3", "4"]; 932 auto y = x.adjacentPairs; 933 assert(equal(y, [t("1", "2"), t("2", "3"), t("3", "4")])); 934 } 935 936 auto rangify(T)(T range) 937 if (__traits(hasMember, T, "length") && 938 __traits(hasMember, T, "opIndex")) 939 { 940 struct Range 941 { 942 T range; 943 ulong _counter; 944 945 bool empty() { return _counter == range.length; } 946 947 auto front() { return range[_counter]; } 948 949 auto popFront() { _counter++; } 950 } 951 952 return Range(range); 953 } 954 955 struct S 956 { 957 int[] arr; 958 auto length() { return arr.length; } 959 int opIndex(size_t i) { return arr[i]; } 960 } 961 962 unittest 963 { 964 import std.algorithm : equal; 965 auto s = S(); 966 s.arr = [1, 2, 3]; 967 assert(equal(s.rangify, [1, 2, 3])); 968 }