1 // Written in the D programming language.
4 This module defines the notion of a range. Ranges generalize the concept of
5 arrays, lists, or anything that involves sequential access. This abstraction
6 enables the same set of algorithms (see $(MREF std, algorithm)) to be used
7 with a vast variety of different concrete types. For example,
8 a linear search algorithm such as $(REF find, std, algorithm, searching)
9 works not just for arrays, but for linked-lists, input files,
10 incoming network data, etc.
14 There are many articles available that can bolster understanding ranges:
17 $(LI Ali Çehreli's $(HTTP ddili.org/ders/d.en/ranges.html, tutorial on ranges)
18 for the basics of working with and creating range-based code.)
19 $(LI Jonathan M. Davis $(LINK2 http://dconf.org/2015/talks/davis.html, $(I Introduction to Ranges))
20 talk at DConf 2015 a vivid introduction from its core constructs to practical advice.)
21 $(LI The DLang Tour's $(LINK2 http://tour.dlang.org/tour/en/basics/ranges, chapter on ranges)
22 for an interactive introduction.)
23 $(LI H. S. Teoh's $(LINK2 http://wiki.dlang.org/Component_programming_with_ranges, tutorial on
24 component programming with ranges) for a real-world showcase of the influence
25 of range-based programming on complex algorithms.)
26 $(LI Andrei Alexandrescu's article
27 $(LINK2 http://www.informit.com/articles/printerfriendly.aspx?p=1407357$(AMP)rll=1,
28 $(I On Iteration)) for conceptual aspect of ranges and the motivation
34 This module has two submodules:
36 The $(MREF std, range, primitives) submodule
37 provides basic range functionality. It defines several templates for testing
38 whether a given object is a range, what kind of range it is, and provides
39 some common range operations.
41 The $(MREF std, range, interfaces) submodule
42 provides object-based interfaces for working with ranges via runtime
45 The remainder of this module provides a rich set of range creation and
46 composition templates that let you construct new ranges out of existing ranges:
49 $(SCRIPT inhibitQuickIndex = 1;)
52 $(TR $(TD $(LREF chain))
53 $(TD Concatenates several ranges into a single range.
55 $(TR $(TD $(LREF choose))
56 $(TD Chooses one of two ranges at runtime based on a boolean condition.
58 $(TR $(TD $(LREF chooseAmong))
59 $(TD Chooses one of several ranges at runtime based on an index.
61 $(TR $(TD $(LREF chunks))
62 $(TD Creates a range that returns fixed-size chunks of the original
65 $(TR $(TD $(LREF cycle))
66 $(TD Creates an infinite range that repeats the given forward range
67 indefinitely. Good for implementing circular buffers.
69 $(TR $(TD $(LREF drop))
70 $(TD Creates the range that results from discarding the first $(I n)
71 elements from the given range.
73 $(TR $(TD $(LREF dropBack))
74 $(TD Creates the range that results from discarding the last $(I n)
75 elements from the given range.
77 $(TR $(TD $(LREF dropExactly))
78 $(TD Creates the range that results from discarding exactly $(I n)
79 of the first elements from the given range.
81 $(TR $(TD $(LREF dropBackExactly))
82 $(TD Creates the range that results from discarding exactly $(I n)
83 of the last elements from the given range.
85 $(TR $(TD $(LREF dropOne))
86 $(TD Creates the range that results from discarding
87 the first element from the given range.
89 $(TR $(TD $(D $(LREF dropBackOne)))
90 $(TD Creates the range that results from discarding
91 the last element from the given range.
93 $(TR $(TD $(LREF enumerate))
94 $(TD Iterates a range with an attached index variable.
96 $(TR $(TD $(LREF evenChunks))
97 $(TD Creates a range that returns a number of chunks of
98 approximately equal length from the original range.
100 $(TR $(TD $(LREF frontTransversal))
101 $(TD Creates a range that iterates over the first elements of the
104 $(TR $(TD $(LREF generate))
105 $(TD Creates a range by successive calls to a given function. This
106 allows to create ranges as a single delegate.
108 $(TR $(TD $(LREF indexed))
109 $(TD Creates a range that offers a view of a given range as though
110 its elements were reordered according to a given range of indices.
112 $(TR $(TD $(LREF iota))
113 $(TD Creates a range consisting of numbers between a starting point
114 and ending point, spaced apart by a given interval.
116 $(TR $(TD $(LREF lockstep))
117 $(TD Iterates $(I n) ranges in lockstep, for use in a `foreach`
118 loop. Similar to `zip`, except that `lockstep` is designed
119 especially for `foreach` loops.
121 $(TR $(TD $(LREF nullSink))
122 $(TD An output range that discards the data it receives.
124 $(TR $(TD $(LREF only))
125 $(TD Creates a range that iterates over the given arguments.
127 $(TR $(TD $(LREF padLeft))
128 $(TD Pads a range to a specified length by adding a given element to
129 the front of the range. Is lazy if the range has a known length.
131 $(TR $(TD $(LREF padRight))
132 $(TD Lazily pads a range to a specified length by adding a given element to
133 the back of the range.
135 $(TR $(TD $(LREF radial))
136 $(TD Given a random-access range and a starting point, creates a
137 range that alternately returns the next left and next right element to
140 $(TR $(TD $(LREF recurrence))
141 $(TD Creates a forward range whose values are defined by a
142 mathematical recurrence relation.
144 $(TR $(TD $(LREF refRange))
145 $(TD Pass a range by reference. Both the original range and the RefRange
146 will always have the exact same elements.
147 Any operation done on one will affect the other.
149 $(TR $(TD $(LREF repeat))
150 $(TD Creates a range that consists of a single element repeated $(I n)
151 times, or an infinite range repeating that element indefinitely.
153 $(TR $(TD $(LREF retro))
154 $(TD Iterates a bidirectional range backwards.
156 $(TR $(TD $(LREF roundRobin))
157 $(TD Given $(I n) ranges, creates a new range that return the $(I n)
158 first elements of each range, in turn, then the second element of each
159 range, and so on, in a round-robin fashion.
161 $(TR $(TD $(LREF sequence))
162 $(TD Similar to `recurrence`, except that a random-access range is
165 $(TR $(TD $(D $(LREF slide)))
166 $(TD Creates a range that returns a fixed-size sliding window
167 over the original range. Unlike chunks,
168 it advances a configurable number of items at a time,
169 not one chunk at a time.
171 $(TR $(TD $(LREF stride))
172 $(TD Iterates a range with stride $(I n).
174 $(TR $(TD $(LREF tail))
175 $(TD Return a range advanced to within `n` elements of the end of
178 $(TR $(TD $(LREF take))
179 $(TD Creates a sub-range consisting of only up to the first $(I n)
180 elements of the given range.
182 $(TR $(TD $(LREF takeExactly))
183 $(TD Like `take`, but assumes the given range actually has $(I n)
184 elements, and therefore also defines the `length` property.
186 $(TR $(TD $(LREF takeNone))
187 $(TD Creates a random-access range consisting of zero elements of the
190 $(TR $(TD $(LREF takeOne))
191 $(TD Creates a random-access range consisting of exactly the first
192 element of the given range.
194 $(TR $(TD $(LREF tee))
195 $(TD Creates a range that wraps a given range, forwarding along
196 its elements while also calling a provided function with each element.
198 $(TR $(TD $(LREF transposed))
199 $(TD Transposes a range of ranges.
201 $(TR $(TD $(LREF transversal))
202 $(TD Creates a range that iterates over the $(I n)'th elements of the
203 given random-access ranges.
205 $(TR $(TD $(LREF zip))
206 $(TD Given $(I n) ranges, creates a range that successively returns a
207 tuple of all the first elements, a tuple of all the second elements,
214 Ranges whose elements are sorted afford better efficiency with certain
215 operations. For this, the $(LREF assumeSorted) function can be used to
216 construct a $(LREF SortedRange) from a pre-sorted range. The $(REF
217 sort, std, algorithm, sorting) function also conveniently
218 returns a $(LREF SortedRange). $(LREF SortedRange) objects provide some additional
219 range operations that take advantage of the fact that the range is sorted.
221 Source: $(PHOBOSSRC std/range/package.d)
223 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
225 Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha,
226 $(HTTP jmdavisprog.com, Jonathan M Davis), and Jack Stouffer. Credit
227 for some of the ideas in building this module goes to
228 $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi).
232 public import std
.array
;
233 public import std
.range
.interfaces
;
234 public import std
.range
.primitives
;
235 public import std
.typecons
: Flag
, Yes
, No
, Rebindable
, rebindable
;
237 import std
.internal
.attributes
: betterC
;
238 import std
.meta
: aliasSeqOf
, allSatisfy
, anySatisfy
, staticMap
;
239 import std
.traits
: CommonType
, isCallable
, isFloatingPoint
, isIntegral
,
240 isPointer
, isSomeFunction
, isStaticArray
, Unqual
, isInstanceOf
;
244 Iterates a bidirectional range backwards. The original range can be
245 accessed by using the `source` property. Applying retro twice to
246 the same range yields the original range.
249 r = the bidirectional range to iterate backwards
252 A bidirectional range with length if `r` also provides a length. Or,
253 if `r` is a random access range, then the return value will be random
256 $(REF reverse, std,algorithm,mutation) for mutating the source range directly.
258 auto retro(Range
)(Range r
)
259 if (isBidirectionalRange
!(Unqual
!Range
))
261 // Check for retro(retro(r)) and just return r in that case
262 static if (is(typeof(retro(r
.source
)) == Range
))
268 static struct Result()
270 private alias R
= Unqual
!Range
;
272 // User code can get and set source, too
275 static if (hasLength
!R
)
277 size_t
retroIndex(size_t n
)
279 return source
.length
- n
- 1;
286 @property bool empty() { return source
.empty
; }
287 @property auto save()
289 return Result(source
.save
);
291 @property auto ref front() { return source
.back
; }
292 void popFront() { source
.popBack(); }
293 @property auto ref back() { return source
.front
; }
294 void popBack() { source
.popFront(); }
296 static if (is(typeof(source
.moveBack())))
298 ElementType
!R
moveFront()
300 return source
.moveBack();
304 static if (is(typeof(source
.moveFront())))
306 ElementType
!R
moveBack()
308 return source
.moveFront();
312 static if (hasAssignableElements
!R
)
314 @property void front(ElementType
!R val
)
316 import core
.lifetime
: forward
;
318 // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542
319 source
.back
= __ctfe ? val
: forward
!val
;
322 @property void back(ElementType
!R val
)
324 import core
.lifetime
: forward
;
326 // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542
327 source
.front
= __ctfe ? val
: forward
!val
;
331 static if (isRandomAccessRange
!(R
) && hasLength
!(R
))
333 auto ref opIndex(size_t n
) { return source
[retroIndex(n
)]; }
335 static if (hasAssignableElements
!R
)
337 void opIndexAssign(ElementType
!R val
, size_t n
)
339 import core
.lifetime
: forward
;
341 // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542
342 source
[retroIndex(n
)] = __ctfe ? val
: forward
!val
;
346 static if (is(typeof(source
.moveAt(0))))
348 ElementType
!R
moveAt(size_t index
)
350 return source
.moveAt(retroIndex(index
));
354 static if (hasSlicing
!R
)
355 typeof(this) opSlice(size_t a
, size_t b
)
357 return typeof(this)(source
[source
.length
- b
.. source
.length
- a
]);
361 mixin ImplementLength
!source
;
370 pure @safe nothrow @nogc unittest
372 import std
.algorithm
.comparison
: equal
;
373 int[5] a
= [ 1, 2, 3, 4, 5 ];
374 int[5] b
= [ 5, 4, 3, 2, 1 ];
375 assert(equal(retro(a
[]), b
[]));
376 assert(retro(a
[]).source
is a
[]);
377 assert(retro(retro(a
[])) is a
[]);
380 pure @safe nothrow unittest
382 import std
.algorithm
.comparison
: equal
;
383 static assert(isBidirectionalRange
!(typeof(retro("hello"))));
385 static assert(is(typeof(a
) == typeof(retro(retro(a
)))));
386 assert(retro(retro(a
)) is a
);
387 static assert(isRandomAccessRange
!(typeof(retro([1, 2, 3]))));
388 void test(int[] input
, int[] witness
)
390 auto r
= retro(input
);
391 assert(r
.front
== witness
.front
);
392 assert(r
.back
== witness
.back
);
393 assert(equal(r
, witness
));
396 test([ 1, 2 ], [ 2, 1 ]);
397 test([ 1, 2, 3 ], [ 3, 2, 1 ]);
398 test([ 1, 2, 3, 4 ], [ 4, 3, 2, 1 ]);
399 test([ 1, 2, 3, 4, 5 ], [ 5, 4, 3, 2, 1 ]);
400 test([ 1, 2, 3, 4, 5, 6 ], [ 6, 5, 4, 3, 2, 1 ]);
402 immutable foo
= [1,2,3].idup
;
404 assert(equal(r
, [3, 2, 1]));
407 pure @safe nothrow unittest
409 import std
.internal
.test.dummyrange
: AllDummyRanges
, propagatesRangeType
,
412 foreach (DummyType
; AllDummyRanges
)
414 static if (!isBidirectionalRange
!DummyType
)
416 static assert(!__traits(compiles
, Retro
!DummyType
));
420 DummyType dummyRange
;
423 auto myRetro
= retro(dummyRange
);
424 static assert(propagatesRangeType
!(typeof(myRetro
), DummyType
));
425 assert(myRetro
.front
== 10);
426 assert(myRetro
.back
== 1);
427 assert(myRetro
.moveFront() == 10);
428 assert(myRetro
.moveBack() == 1);
430 static if (isRandomAccessRange
!DummyType
&& hasLength
!DummyType
)
432 assert(myRetro
[0] == myRetro
.front
);
433 assert(myRetro
.moveAt(2) == 8);
435 static if (DummyType
.r
== ReturnBy
.Reference
)
439 scope(exit
) myRetro
[9]--;
440 assert(dummyRange
[0] == 2);
442 scope(exit
) myRetro
.front
--;
443 assert(myRetro
.front
== 11);
445 scope(exit
) myRetro
.back
--;
446 assert(myRetro
.back
== 3);
450 myRetro
.front
= 0xFF;
451 scope(exit
) myRetro
.front
= 10;
452 assert(dummyRange
.back
== 0xFF);
455 scope(exit
) myRetro
.back
= 1;
456 assert(dummyRange
.front
== 0xBB);
459 scope(exit
) myRetro
[1] = 8;
460 assert(dummyRange
[8] == 11);
468 pure @safe nothrow @nogc unittest
470 import std
.algorithm
.comparison
: equal
;
471 auto LL
= iota(1L, 4L);
473 long[3] excepted
= [3, 2, 1];
474 assert(equal(r
, excepted
[]));
477 // https://issues.dlang.org/show_bug.cgi?id=12662
478 pure @safe nothrow @nogc unittest
480 int[3] src
= [1,2,3];
482 foreach_reverse (x
; data
) {}
483 foreach (x
; data
.retro
) {}
486 pure @safe nothrow unittest
488 import std
.algorithm
.comparison
: equal
;
495 immutable foo
= [S(1), S(2), S(3)];
497 assert(equal(r
, [S(3), S(2), S(1)]));
500 // https://issues.dlang.org/show_bug.cgi?id=24481
507 void opAssign()(auto ref const(typeof(this)) that
) const { called
= true; }
510 const(Handle
)[5] arr
= [Handle(0), Handle(1), Handle(2), Handle(3), Handle(4)];
511 auto range
= arr
[].retro();
514 range
.front
= Handle(42);
518 range
.back
= Handle(42);
522 range
[2] = Handle(42);
527 Iterates range `r` with stride `n`. If the range is a
528 random-access range, moves by indexing into the range; otherwise,
529 moves by successive calls to `popFront`. Applying stride twice to
530 the same range results in a stride with a step that is the
531 product of the two applications. It is an error for `n` to be 0.
534 r = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to stride over
535 n = the number of elements to skip over
538 At minimum, an input range. The resulting range will adopt the
539 range primitives of the underlying range as long as
540 $(REF hasLength, std,range,primitives) is `true`.
542 auto stride(Range
)(Range r
, size_t n
)
543 if (isInputRange
!(Unqual
!Range
))
546 assert(n
!= 0, "stride cannot have step zero.");
550 import std
.algorithm
.comparison
: min
;
552 static if (is(typeof(stride(r
.source
, n
)) == Range
))
554 // stride(stride(r, n1), n2) is stride(r, n1 * n2)
555 return stride(r
.source
, r
._n
* n
);
561 private alias R
= Unqual
!Range
;
565 // Chop off the slack elements at the end
566 static if (hasLength
!R
&&
567 (isRandomAccessRange
!R
&& hasSlicing
!R
568 || isBidirectionalRange
!R
))
569 private void eliminateSlackElements()
571 auto slack
= source
.length
% _n
;
577 else if (!source
.empty
)
579 slack
= min(_n
, source
.length
) - 1;
586 static if (isRandomAccessRange
!R
&& hasLength
!R
&& hasSlicing
!R
)
588 source
= source
[0 .. source
.length
- slack
];
590 else static if (isBidirectionalRange
!R
)
592 foreach (i
; 0 .. slack
)
599 static if (isForwardRange
!R
)
601 @property auto save()
603 return Result(source
.save
, _n
);
607 static if (isInfinite
!R
)
609 enum bool empty
= false;
613 @property bool empty()
619 @property auto ref front()
624 static if (is(typeof(.moveFront(source
))))
626 ElementType
!R
moveFront()
628 return source
.moveFront();
632 static if (hasAssignableElements
!R
)
634 @property void front(ElementType
!R val
)
636 import core
.lifetime
: forward
;
638 // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542
639 source
.front
= __ctfe ? val
: forward
!val
;
645 source
.popFrontN(_n
);
648 static if (isBidirectionalRange
!R
&& hasLength
!R
)
652 popBackN(source
, _n
);
655 @property auto ref back()
657 eliminateSlackElements();
661 static if (is(typeof(.moveBack(source
))))
663 ElementType
!R
moveBack()
665 eliminateSlackElements();
666 return source
.moveBack();
670 static if (hasAssignableElements
!R
)
672 @property void back(ElementType
!R val
)
674 eliminateSlackElements();
680 static if (isRandomAccessRange
!R
&& hasLength
!R
)
682 auto ref opIndex(size_t n
)
684 return source
[_n
* n
];
688 Forwards to $(D moveAt(source, n)).
690 static if (is(typeof(source
.moveAt(0))))
692 ElementType
!R
moveAt(size_t n
)
694 return source
.moveAt(_n
* n
);
698 static if (hasAssignableElements
!R
)
700 void opIndexAssign(ElementType
!R val
, size_t n
)
702 source
[_n
* n
] = val
;
707 static if (hasSlicing
!R
&& hasLength
!R
)
708 typeof(this) opSlice(size_t lower
, size_t upper
)
710 assert(upper
>= lower
&& upper
<= length
,
711 "Attempt to get out-of-bounds slice of `stride` range");
712 immutable translatedUpper
= (upper
== 0) ?
0 :
713 (upper
* _n
- (_n
- 1));
714 immutable translatedLower
= min(lower
* _n
, translatedUpper
);
716 assert(translatedLower
<= translatedUpper
,
717 "Overflow when calculating slice of `stride` range");
719 return typeof(this)(source
[translatedLower
.. translatedUpper
], _n
);
722 static if (hasLength
!R
)
724 @property auto length()
726 return (source
.length
+ _n
- 1) / _n
;
729 alias opDollar
= length
;
737 pure @safe nothrow unittest
739 import std
.algorithm
.comparison
: equal
;
741 int[] a
= [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ];
742 assert(equal(stride(a
, 3), [ 1, 4, 7, 10 ][]));
743 assert(stride(stride(a
, 2), 3) == stride(a
, 6));
746 pure @safe nothrow @nogc unittest
748 import std
.algorithm
.comparison
: equal
;
750 int[4] testArr
= [1,2,3,4];
751 static immutable result
= [1, 3];
752 assert(equal(testArr
[].stride(2), result
));
755 debug pure nothrow @system unittest
756 {//check the contract
757 int[4] testArr
= [1,2,3,4];
759 scope (success
) assert(passed
);
760 import core
.exception
: AssertError
;
761 //std.exception.assertThrown won't do because it can't infer nothrow
762 // https://issues.dlang.org/show_bug.cgi?id=12647
765 auto unused
= testArr
[].stride(0);
767 catch (AssertError unused
)
773 pure @safe nothrow unittest
775 import std
.algorithm
.comparison
: equal
;
776 import std
.internal
.test.dummyrange
: AllDummyRanges
, propagatesRangeType
,
779 static assert(isRandomAccessRange
!(typeof(stride([1, 2, 3], 2))));
780 void test(size_t n
, int[] input
, int[] witness
)
782 assert(equal(stride(input
, n
), witness
));
785 int[] arr
= [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
786 assert(stride(stride(arr
, 2), 3) is stride(arr
, 6));
788 test(2, arr
, [1, 3, 5, 7, 9]);
789 test(3, arr
, [1, 4, 7, 10]);
790 test(4, arr
, [1, 5, 9]);
793 auto s1
= stride(arr
, 1);
794 assert(equal(s1
[1 .. 4], [2, 3, 4]));
795 assert(s1
[1 .. 4].length
== 3);
796 assert(equal(s1
[1 .. 5], [2, 3, 4, 5]));
797 assert(s1
[1 .. 5].length
== 4);
798 assert(s1
[0 .. 0].empty
);
799 assert(s1
[3 .. 3].empty
);
800 // assert(s1[$ .. $].empty);
801 assert(s1
[s1
.opDollar
.. s1
.opDollar
].empty
);
803 auto s2
= stride(arr
, 2);
804 assert(equal(s2
[0 .. 2], [1,3]));
805 assert(s2
[0 .. 2].length
== 2);
806 assert(equal(s2
[1 .. 5], [3, 5, 7, 9]));
807 assert(s2
[1 .. 5].length
== 4);
808 assert(s2
[0 .. 0].empty
);
809 assert(s2
[3 .. 3].empty
);
810 // assert(s2[$ .. $].empty);
811 assert(s2
[s2
.opDollar
.. s2
.opDollar
].empty
);
813 // Test fix for https://issues.dlang.org/show_bug.cgi?id=5035
814 auto m
= [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]; // 3 rows, 4 columns
815 auto col
= stride(m
, 4);
816 assert(equal(col
, [1, 1, 1]));
817 assert(equal(retro(col
), [1, 1, 1]));
819 immutable int[] immi
= [ 1, 2, 3 ];
820 static assert(isRandomAccessRange
!(typeof(stride(immi
, 1))));
822 // Check for infiniteness propagation.
823 static assert(isInfinite
!(typeof(stride(repeat(1), 3))));
825 foreach (DummyType
; AllDummyRanges
)
827 DummyType dummyRange
;
830 auto myStride
= stride(dummyRange
, 4);
832 // Should fail if no length and bidirectional b/c there's no way
833 // to know how much slack we have.
834 static if (hasLength
!DummyType ||
!isBidirectionalRange
!DummyType
)
836 static assert(propagatesRangeType
!(typeof(myStride
), DummyType
));
838 assert(myStride
.front
== 1);
839 assert(myStride
.moveFront() == 1);
840 assert(equal(myStride
, [1, 5, 9]));
842 static if (hasLength
!DummyType
)
844 assert(myStride
.length
== 3);
847 static if (isBidirectionalRange
!DummyType
&& hasLength
!DummyType
)
849 assert(myStride
.back
== 9);
850 assert(myStride
.moveBack() == 9);
853 static if (isRandomAccessRange
!DummyType
&& hasLength
!DummyType
)
855 assert(myStride
[0] == 1);
856 assert(myStride
[1] == 5);
857 assert(myStride
.moveAt(1) == 5);
858 assert(myStride
[2] == 9);
860 static assert(hasSlicing
!(typeof(myStride
)));
863 static if (DummyType
.r
== ReturnBy
.Reference
)
865 // Make sure reference is propagated.
869 scope(exit
) myStride
.front
--;
870 assert(dummyRange
.front
== 2);
874 scope(exit
) myStride
.front
= 1;
875 assert(dummyRange
.front
== 4);
878 static if (isBidirectionalRange
!DummyType
&& hasLength
!DummyType
)
882 scope(exit
) myStride
.back
--;
883 assert(myStride
.back
== 10);
887 scope(exit
) myStride
.back
= 9;
888 assert(myStride
.back
== 111);
891 static if (isRandomAccessRange
!DummyType
)
895 scope(exit
) myStride
[1]--;
896 assert(dummyRange
[4] == 6);
900 scope(exit
) myStride
[1] = 5;
901 assert(dummyRange
[4] == 55);
909 pure @safe nothrow unittest
911 import std
.algorithm
.comparison
: equal
;
913 auto LL
= iota(1L, 10L);
914 auto s
= stride(LL
, 3);
915 assert(equal(s
, [1L, 4L, 7L]));
918 pure @safe nothrow unittest
920 import std
.algorithm
.comparison
: equal
;
927 immutable foo
= [S(1), S(2), S(3), S(4), S(5)];
928 auto r
= stride(foo
, 3);
929 assert(equal(r
, [S(1), S(4)]));
932 // https://issues.dlang.org/show_bug.cgi?id=24481
939 void opAssign()(auto ref const(typeof(this)) that
) const { called
= true; }
942 const(Handle
)[5] arr
= [Handle(0), Handle(1), Handle(2), Handle(3), Handle(4)];
943 auto range
= arr
[].stride(2);
946 range
.front
= Handle(42);
951 Spans multiple ranges in sequence. The function `chain` takes any
952 number of ranges and returns a $(D Chain!(R1, R2,...)) object. The
953 ranges may be different, but they must have the same element type. The
954 result is a range that offers the `front`, `popFront`, and $(D
955 empty) primitives. If all input ranges offer random access and $(D
956 length), `Chain` offers them as well.
958 Note that repeated random access of the resulting range is likely
959 to perform somewhat badly since lengths of the ranges in the chain have to be
960 added up for each random access operation. Random access to elements of
961 the first remaining range is still efficient.
963 If only one range is offered to `Chain` or `chain`, the $(D
964 Chain) type exits the picture by aliasing itself directly to that
968 rs = the $(REF_ALTTEXT input ranges, isInputRange, std,range,primitives) to chain together
971 An input range at minimum. If all of the ranges in `rs` provide
972 a range primitive, the returned range will also provide that range
975 See_Also: $(LREF only) to chain values to a range
977 auto chain(Ranges
...)(Ranges rs
)
978 if (Ranges
.length
> 0 &&
979 allSatisfy
!(isInputRange
, staticMap
!(Unqual
, Ranges
)) &&
980 !is(CommonType
!(staticMap
!(ElementType
, staticMap
!(Unqual
, Ranges
))) == void))
982 static if (Ranges
.length
== 1)
991 alias R
= staticMap
!(Unqual
, Ranges
);
992 alias RvalueElementType
= CommonType
!(staticMap
!(.ElementType
, R
));
995 enum sameET
= is(.ElementType
!A
== RvalueElementType
);
998 enum bool allSameType
= allSatisfy
!(sameET
, R
),
999 bidirectional
= allSatisfy
!(isBidirectionalRange
, R
),
1000 mobileElements
= allSatisfy
!(hasMobileElements
, R
),
1001 assignableElements
= allSameType
1002 && allSatisfy
!(hasAssignableElements
, R
);
1004 alias ElementType
= RvalueElementType
;
1006 static if (allSameType
&& allSatisfy
!(hasLvalueElements
, R
))
1008 static ref RvalueElementType
fixRef(ref RvalueElementType val
)
1015 static RvalueElementType
fixRef(RvalueElementType val
)
1023 // Always points to index one past the last non-empty range,
1024 // because otherwise decrementing while pointing to first range
1025 // would overflow to size_t.max.
1026 static if (bidirectional
) size_t backIndex
;
1027 else enum backIndex
= source
.length
;
1029 this(typeof(Result
.tupleof
) fields
)
1031 this.tupleof
= fields
;
1037 frontIndex
= source
.length
;
1038 static if (bidirectional
) backIndex
= 0;
1040 foreach (i
, ref v
; input
) source
[i
] = v
;
1042 // We do this separately to avoid invoking `empty` needlessly.
1043 // While not recommended, a range may depend on side effects of
1045 foreach (i
, ref v
; input
) if (!v
.empty
)
1048 static if (bidirectional
) backIndex
= i
+1;
1052 // backIndex is already set in the first loop to
1053 // as frontIndex+1, so we'll use that if we don't find a
1054 // non-empty range here.
1055 static if (bidirectional
)
1056 static foreach_reverse (i
; 1 .. R
.length
+ 1)
1058 if (i
<= frontIndex
+ 1) return;
1059 if (!input
[i
-1].empty
)
1067 import std
.meta
: anySatisfy
;
1069 static if (anySatisfy
!(isInfinite
, R
))
1071 // Propagate infiniteness.
1072 enum bool empty
= false;
1076 @property bool empty()
1078 if (frontIndex
== 0)
1080 // special handling: we might be in Range.init state!
1081 // For instance, `format!"%s"` uses Range.init to ensure
1082 // that formatting is possible.
1083 // In that case, we must still behave in an internally consistent way.
1084 return source
[0].empty
;
1086 return frontIndex
>= backIndex
;
1090 static if (allSatisfy
!(isForwardRange
, R
))
1092 @property auto save()
1094 auto saveI(size_t i
)() => source
[i
].save
;
1096 // TODO: this has the constructor needlessly refind
1097 // frontIndex and backIndex. It'd be better to just copy
1098 // those from `.this`.
1100 Result(staticMap
!(saveI
, aliasSeqOf
!(R
.length
.iota
)));
1108 sw1
: switch (frontIndex
)
1110 static foreach (i
; 0 .. R
.length
)
1113 source
[i
].popFront();
1118 assert(0, "Attempt to `popFront` of empty `chain` range");
1121 assert(0, "Internal library error. Please report it.");
1124 sw2
: switch (frontIndex
)
1126 static foreach (i
; 0 .. R
.length
)
1129 if (source
[i
].empty
)
1137 // Only possible to reach from goto of previous case.
1142 assert(0, "Internal library error. Please report it.");
1146 @property auto ref front()
1150 static foreach (i
; 0 .. R
.length
)
1153 return fixRef(source
[i
].front
);
1157 assert(0, "Attempt to get `front` of empty `chain` range");
1160 assert(0, "Internal library error. Please report it.");
1164 static if (assignableElements
)
1167 //@property void front(T)(T v) if (is(T : RvalueElementType))
1169 @property void front(RvalueElementType v
)
1171 import core
.lifetime
: forward
;
1173 sw
: switch (frontIndex
)
1175 static foreach (i
; 0 .. R
.length
)
1178 // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542
1179 source
[i
].front
= __ctfe ? v
: forward
!v
;
1184 assert(0, "Attempt to set `front` of empty `chain` range");
1187 assert(0, "Internal library error. Please report it.");
1192 static if (mobileElements
)
1194 RvalueElementType
moveFront()
1198 static foreach (i
; 0 .. R
.length
)
1201 return source
[i
].moveFront();
1205 assert(0, "Attempt to `moveFront` of empty `chain` range");
1208 assert(0, "Internal library error. Please report it.");
1213 static if (bidirectional
)
1215 @property auto ref back()
1219 static foreach_reverse (i
; 1 .. R
.length
+ 1)
1222 return fixRef(source
[i
-1].back
);
1226 assert(0, "Attempt to get `back` of empty `chain` range");
1229 assert(0, "Internal library error. Please report it.");
1235 sw1
: switch (backIndex
)
1237 static foreach_reverse (i
; 1 .. R
.length
+ 1)
1240 source
[i
-1].popBack();
1245 assert(0, "Attempt to `popFront` of empty `chain` range");
1248 assert(0, "Internal library error. Please report it.");
1251 sw2
: switch (backIndex
)
1253 static foreach_reverse (i
; 1 .. R
.length
+ 1)
1256 if (source
[i
-1].empty
)
1264 // Only possible to reach from goto of previous case.
1269 assert(0, "Internal library error. Please report it.");
1273 static if (mobileElements
)
1275 RvalueElementType
moveBack()
1279 static foreach_reverse (i
; 1 .. R
.length
+ 1)
1282 return source
[i
-1].moveBack();
1286 assert(0, "Attempt to `moveBack` of empty `chain` range");
1289 assert(0, "Internal library error. Please report it.");
1294 static if (allSameType
&& allSatisfy
!(hasAssignableElements
, R
))
1296 @property void back(RvalueElementType v
)
1298 import core
.lifetime
: forward
;
1300 sw
: switch (backIndex
)
1302 static foreach_reverse (i
; 1 .. R
.length
+ 1)
1305 // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542
1306 source
[i
- 1].back
= __ctfe ? v
: forward
!v
;
1311 assert(0, "Attempt to set `back` of empty `chain` range");
1314 assert(0, "Internal library error. Please report it.");
1320 static if (allSatisfy
!(hasLength
, R
))
1322 @property size_t
length()
1325 sw
: switch (frontIndex
)
1327 static foreach (i
; 0 .. R
.length
)
1330 result
+= source
[i
].length
;
1331 if (backIndex
== i
+1) break sw
;
1339 assert(0, "Internal library error. Please report it.");
1345 alias opDollar
= length
;
1348 static if (allSatisfy
!(isRandomAccessRange
, R
))
1350 auto ref opIndex(size_t index
)
1354 static foreach (i
; 0 .. R
.length
)
1357 static if (!isInfinite
!(R
[i
]))
1359 immutable length
= source
[i
].length
;
1360 if (index
>= length
)
1367 return fixRef(source
[i
][index
]);
1371 assert(0, "Attempt to access out-of-bounds index of `chain` range");
1374 assert(0, "Internal library error. Please report it.");
1378 static if (mobileElements
)
1380 RvalueElementType
moveAt(size_t index
)
1384 static foreach (i
; 0 .. R
.length
)
1387 static if (!isInfinite
!(R
[i
]))
1389 immutable length
= source
[i
].length
;
1390 if (index
>= length
)
1397 return source
[i
].moveAt(index
);
1401 assert(0, "Attempt to move out-of-bounds index of `chain` range");
1404 assert(0, "Internal library error. Please report it.");
1409 static if (allSameType
&& allSatisfy
!(hasAssignableElements
, R
))
1410 void opIndexAssign(ElementType v
, size_t index
)
1412 import core
.lifetime
: forward
;
1414 sw
: switch (frontIndex
)
1416 static foreach (i
; 0 .. R
.length
)
1419 static if (!isInfinite
!(R
[i
]))
1421 immutable length
= source
[i
].length
;
1422 if (index
>= length
)
1429 // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542
1430 source
[i
][index
] = __ctfe ? v
: forward
!v
;
1435 assert(0, "Attempt to write out-of-bounds index of `chain` range");
1438 assert(0, "Internal library error. Please report it.");
1443 static if (allSatisfy
!(hasLength
, R
) && allSatisfy
!(hasSlicing
, R
))
1444 auto opSlice(size_t begin
, size_t end
) return scope
1446 // force staticMap type conversion to Rebindable
1447 static struct ResultRanges
1449 staticMap
!(Rebindable
, typeof(source
)) fields
;
1451 auto sourceI(size_t i
)() => rebindable(this.source
[i
]);
1452 auto resultRanges
= ResultRanges(staticMap
!(sourceI
, aliasSeqOf
!(R
.length
.iota
))).fields
;
1453 size_t resultFrontIndex
= this.frontIndex
;
1454 static if (bidirectional
)
1455 size_t resultBackIndex
= this.backIndex
;
1457 sw
: switch (frontIndex
)
1459 static foreach (i
; 0 .. R
.length
)
1462 immutable len
= resultRanges
[i
].length
;
1465 resultRanges
[i
] = resultRanges
[i
]
1473 resultRanges
[i
] = resultRanges
[i
]
1481 "Attempt to access out-of-bounds slice of `chain` range");
1485 assert(0, "Internal library error. Please report it.");
1488 // Overflow intentional if end index too big.
1489 // This will trigger the bounds check failure below.
1490 auto cut
= length
- end
;
1492 sw2
: switch (backIndex
)
1494 static foreach_reverse (i
; 1 .. R
.length
+ 1)
1497 immutable len
= resultRanges
[i
-1].length
;
1500 resultRanges
[i
-1] = resultRanges
[i
-1]
1508 resultRanges
[i
-1] = resultRanges
[i
-1]
1515 assert(cut
== 0, end
> length?
1516 "Attempt to access out-of-bounds slice of `chain` range":
1517 "Attempt to access negative length slice of `chain` range");
1521 assert(0, "Internal library error. Please report it.");
1524 static if (bidirectional
)
1525 return Result(resultRanges
, resultFrontIndex
, resultBackIndex
);
1527 return Result(resultRanges
, resultFrontIndex
);
1535 pure @safe nothrow unittest
1537 import std
.algorithm
.comparison
: equal
;
1539 int[] arr1
= [ 1, 2, 3, 4 ];
1540 int[] arr2
= [ 5, 6 ];
1542 auto s
= chain(arr1
, arr2
, arr3
);
1543 assert(s
.length
== 7);
1545 assert(equal(s
, [1, 2, 3, 4, 5, 6, 7][]));
1549 * Range primitives are carried over to the returned range if
1550 * all of the ranges provide them
1552 pure @safe nothrow unittest
1554 import std
.algorithm
.comparison
: equal
;
1555 import std
.algorithm
.sorting
: sort
;
1557 int[] arr1
= [5, 2, 8];
1558 int[] arr2
= [3, 7, 9];
1559 int[] arr3
= [1, 4, 6];
1561 // in-place sorting across all of the arrays
1562 auto s
= arr1
.chain(arr2
, arr3
).sort
;
1564 assert(s
.equal([1, 2, 3, 4, 5, 6, 7, 8, 9]));
1565 assert(arr1
.equal([1, 2, 3]));
1566 assert(arr2
.equal([4, 5, 6]));
1567 assert(arr3
.equal([7, 8, 9]));
1571 Due to safe type promotion in D, chaining together different
1572 character ranges results in a `uint` range.
1574 Use $(REF_ALTTEXT byChar, byChar,std,utf), $(REF_ALTTEXT byWchar, byWchar,std,utf),
1575 and $(REF_ALTTEXT byDchar, byDchar,std,utf) on the ranges
1576 to get the type you need.
1578 pure @safe nothrow unittest
1580 import std
.utf
: byChar
, byCodeUnit
;
1582 auto s1
= "string one";
1583 auto s2
= "string two";
1584 // s1 and s2 front is dchar because of auto-decoding
1585 static assert(is(typeof(s1
.front
) == dchar) && is(typeof(s2
.front
) == dchar));
1587 auto r1
= s1
.chain(s2
);
1588 // chains of ranges of the same character type give that same type
1589 static assert(is(typeof(r1
.front
) == dchar));
1591 auto s3
= "string three".byCodeUnit
;
1592 static assert(is(typeof(s3
.front
) == immutable char));
1593 auto r2
= s1
.chain(s3
);
1594 // chaining ranges of mixed character types gives `dchar`
1595 static assert(is(typeof(r2
.front
) == dchar));
1597 // use byChar on character ranges to correctly convert them to UTF-8
1598 auto r3
= s1
.byChar
.chain(s3
);
1599 static assert(is(typeof(r3
.front
) == immutable char));
1602 pure @safe nothrow unittest
1604 import std
.algorithm
.comparison
: equal
;
1605 import std
.internal
.test.dummyrange
: AllDummyRanges
, dummyLength
,
1606 propagatesRangeType
;
1609 int[] arr1
= [ 1, 2, 3, 4 ];
1610 int[] arr2
= [ 5, 6 ];
1612 int[] witness
= [ 1, 2, 3, 4, 5, 6, 7 ];
1613 auto s1
= chain(arr1
);
1614 static assert(isRandomAccessRange
!(typeof(s1
)));
1615 auto s2
= chain(arr1
, arr2
);
1616 static assert(isBidirectionalRange
!(typeof(s2
)));
1617 static assert(isRandomAccessRange
!(typeof(s2
)));
1619 auto s
= chain(arr1
, arr2
, arr3
);
1621 assert(equal(s
, witness
));
1622 assert(s
[4 .. 6].equal(arr2
));
1623 assert(s
[2 .. 5].equal([3, 4, 5]));
1624 assert(s
[0 .. 0].empty
);
1625 assert(s
[7 .. $].empty
);
1629 int[] arr1
= [ 1, 2, 3, 4 ];
1630 int[] witness
= [ 1, 2, 3, 4 ];
1631 assert(equal(chain(arr1
), witness
));
1634 uint[] foo
= [1,2,3,4,5];
1635 uint[] bar
= [1,2,3,4,5];
1636 auto c
= chain(foo
, bar
);
1639 assert(c
.moveFront() == 1);
1640 assert(c
.moveBack() == 5);
1641 assert(c
.moveAt(4) == 5);
1642 assert(c
.moveAt(5) == 1);
1646 // Make sure https://issues.dlang.org/show_bug.cgi?id=3311 is fixed.
1647 // elements are mutable.
1648 assert(equal(chain(iota(0, 3), iota(0, 3)), [0, 1, 2, 0, 1, 2]));
1650 // Test the case where infinite ranges are present.
1651 auto inf
= chain([0,1,2][], cycle([4,5,6][]), [7,8,9][]); // infinite range
1652 assert(inf
[0] == 0);
1653 assert(inf
[3] == 4);
1654 assert(inf
[6] == 4);
1655 assert(inf
[7] == 5);
1656 static assert(isInfinite
!(typeof(inf
)));
1658 immutable int[] immi
= [ 1, 2, 3 ];
1659 immutable float[] immf
= [ 1, 2, 3 ];
1660 static assert(is(typeof(chain(immi
, immf
))));
1662 // Check that chain at least instantiates and compiles with every possible
1663 // pair of DummyRange types, in either order.
1665 foreach (DummyType1
; AllDummyRanges
)
1666 (){ // workaround slow optimizations for large functions
1667 // https://issues.dlang.org/show_bug.cgi?id=2396
1669 foreach (DummyType2
; AllDummyRanges
)
1672 auto myChain
= chain(dummy1
, dummy2
);
1675 propagatesRangeType
!(typeof(myChain
), DummyType1
, DummyType2
)
1678 assert(myChain
.front
== 1);
1679 foreach (i
; 0 .. dummyLength
)
1683 assert(myChain
.front
== 1);
1685 static if (isBidirectionalRange
!DummyType1
&&
1686 isBidirectionalRange
!DummyType2
) {
1687 assert(myChain
.back
== 10);
1690 static if (isRandomAccessRange
!DummyType1
&&
1691 isRandomAccessRange
!DummyType2
) {
1692 assert(myChain
[0] == 1);
1695 static if (hasLvalueElements
!DummyType1
&& hasLvalueElements
!DummyType2
)
1697 static assert(hasLvalueElements
!(typeof(myChain
)));
1701 static assert(!hasLvalueElements
!(typeof(myChain
)));
1707 pure @safe nothrow @nogc unittest
1712 assert(chain(a
, b
).empty
);
1715 // https://issues.dlang.org/show_bug.cgi?id=18657
1718 import std
.algorithm
.comparison
: equal
;
1720 auto r
= refRange(&s
).chain("bar");
1721 assert(equal(r
.save
, "foobar"));
1722 assert(equal(r
, "foobar"));
1725 // https://issues.dlang.org/show_bug.cgi?id=23844
1730 immutable int value
;
1733 auto range
= chain(only(S(5)), only(S(6)));
1734 assert(range
.array
== [S(5), S(6)]);
1737 // https://issues.dlang.org/show_bug.cgi?id=24064
1738 pure @safe nothrow unittest
1740 import std
.algorithm
.comparison
: equal
;
1741 import std
.typecons
: Nullable
;
1743 immutable Nullable
!string foo
= "b";
1744 string
[] bar
= ["a"];
1745 assert(chain(bar
, foo
).equal(["a", "b"]));
1748 pure @safe nothrow @nogc unittest
1750 // support non-copyable items
1754 @disable this(this);
1758 foreach (ref el
; chain(s0
[], s1
[]))
1764 foreach (ref el
; chain(s2
, s3
))
1770 // https://issues.dlang.org/show_bug.cgi?id=24243
1771 pure @safe nothrow unittest
1773 import std
.algorithm
.iteration
: filter
;
1775 auto range
= chain([2], [3].filter
!"a");
1777 // This might happen in format!"%s"(range), for instance.
1778 assert(typeof(range
).init
.empty
);
1781 // https://issues.dlang.org/show_bug.cgi?id=24481
1788 void opAssign()(auto ref const(typeof(this)) that
) const { called
= true; }
1791 const(Handle
)[5] arr
= [Handle(0), Handle(1), Handle(2), Handle(3), Handle(4)];
1792 auto range
= arr
[0 .. 2].chain(arr
[4 .. 5]);
1795 range
.front
= Handle(42);
1799 range
.back
= Handle(42);
1803 range
[2] = Handle(42);
1808 Choose one of two ranges at runtime depending on a Boolean condition.
1810 The ranges may be different, but they must have compatible element types (i.e.
1811 `CommonType` must exist for the two element types). The result is a range
1812 that offers the weakest capabilities of the two (e.g. `ForwardRange` if $(D
1813 R1) is a random-access range and `R2` is a forward range).
1816 condition = which range to choose: `r1` if `true`, `r2` otherwise
1817 r1 = the "true" range
1818 r2 = the "false" range
1821 A range type dependent on `R1` and `R2`.
1823 auto choose(R1
, R2
)(bool condition
, return scope R1 r1
, return scope R2 r2
)
1824 if (isInputRange
!(Unqual
!R1
) && isInputRange
!(Unqual
!R2
) &&
1825 !is(CommonType
!(ElementType
!(Unqual
!R1
), ElementType
!(Unqual
!R2
)) == void))
1827 size_t choice
= condition?
0: 1;
1828 return ChooseResult
!(R1
, R2
)(choice
, r1
, r2
);
1832 @safe nothrow pure @nogc unittest
1834 import std
.algorithm
.comparison
: equal
;
1835 import std
.algorithm
.iteration
: filter
, map
;
1837 auto data1
= only(1, 2, 3, 4).filter
!(a
=> a
!= 3);
1838 auto data2
= only(5, 6, 7, 8).map
!(a
=> a
+ 1);
1840 // choose() is primarily useful when you need to select one of two ranges
1841 // with different types at runtime.
1842 static assert(!is(typeof(data1
) == typeof(data2
)));
1844 auto chooseRange(bool pickFirst
)
1846 // The returned range is a common wrapper type that can be used for
1847 // returning or storing either range without running into a type error.
1848 return choose(pickFirst
, data1
, data2
);
1850 // Simply returning the chosen range without using choose() does not
1851 // work, because map() and filter() return different types.
1852 //return pickFirst ? data1 : data2; // does not compile
1855 auto result
= chooseRange(true);
1856 assert(result
.equal(only(1, 2, 4)));
1858 result
= chooseRange(false);
1859 assert(result
.equal(only(6, 7, 8, 9)));
1863 private struct ChooseResult(Ranges
...)
1865 import std
.meta
: aliasSeqOf
, ApplyLeft
;
1866 import std
.traits
: hasElaborateCopyConstructor
, hasElaborateDestructor
,
1873 private size_t chosenI
;
1875 private static auto ref actOnChosen(alias foo
, ExtraArgs
...)
1876 (ref ChooseResult r
, auto ref ExtraArgs extraArgs
)
1878 ref getI(size_t i
)(return ref ChooseResult r
) @trusted { return r
.rs
[i
]; }
1882 static foreach (candI
; 0 .. rs
.length
)
1884 case candI
: return foo(getI
!candI(r
), extraArgs
);
1887 default: assert(false);
1891 // @trusted because of assignment of r which overlap each other
1892 this(size_t chosen
, return scope Ranges rs
) @trusted
1894 import core
.lifetime
: emplace
;
1896 // This should be the only place chosenI is ever assigned
1898 this.chosenI
= chosen
;
1900 // Otherwise the compiler will complain about skipping these fields
1901 static foreach (i
; 0 .. rs
.length
)
1903 this.rs
[i
] = Ranges
[i
].init
;
1906 // The relevant field needs to be initialized last so it will overwrite
1907 // the other initializations and not the other way around.
1908 sw
: switch (chosenI
)
1910 static foreach (i
; 0 .. rs
.length
)
1913 emplace(&this.rs
[i
], rs
[i
]);
1917 default: assert(false);
1921 // Some legacy code may still call this with typeof(choose(/*...*/))(/*...*/)
1922 // without this overload the regular constructor would invert the meaning of
1924 static if (rs
.length
== 2)
1925 pragma(inline
, true)
1926 deprecated("Call with size_t (0 = first), or use the choose function")
1927 this(bool firstChosen
, Ranges rs
)
1929 import core
.lifetime
: move
;
1930 this(cast(size_t
)(firstChosen?
0: 1), rs
[0].move
, rs
[1].move
);
1933 void opAssign(ChooseResult r
)
1935 ref getI(size_t i
)(return ref ChooseResult r
) @trusted { return r
.rs
[i
]; }
1937 static if (anySatisfy
!(hasElaborateDestructor
, Ranges
))
1938 if (chosenI
!= r
.chosenI
)
1940 // destroy the current item
1941 actOnChosen
!((ref r
) => destroy(r
))(this);
1943 chosenI
= r
.chosenI
;
1945 sw
: switch (chosenI
)
1947 static foreach (candI
; 0 .. rs
.length
)
1949 case candI
: getI
!candI(this) = getI
!candI(r
);
1953 default: assert(false);
1957 // Carefully defined postblit to postblit the appropriate range
1958 static if (anySatisfy
!(hasElaborateCopyConstructor
, Ranges
))
1961 actOnChosen
!((ref r
) {
1962 static if (hasElaborateCopyConstructor
!(typeof(r
))) r
.__xpostblit();
1966 static if (anySatisfy
!(hasElaborateDestructor
, Ranges
))
1969 actOnChosen
!((ref r
) => destroy(r
))(this);
1972 // Propagate infiniteness.
1973 static if (allSatisfy
!(isInfinite
, Ranges
)) enum bool empty
= false;
1974 else @property bool empty()
1976 return actOnChosen
!(r
=> r
.empty
)(this);
1979 @property auto ref front()
1981 static auto ref getFront(R
)(ref R r
) { return r
.front
; }
1982 return actOnChosen
!getFront(this);
1987 return actOnChosen
!((ref r
) { r
.popFront
; })(this);
1990 static if (allSatisfy
!(isForwardRange
, Ranges
))
1991 @property auto save() // return scope inferred
1993 auto saveOrInit(size_t i
)()
1995 ref getI() @trusted { return rs
[i
]; }
1996 if (i
== chosenI
) return getI().save
;
1997 else return Ranges
[i
].init
;
2000 return typeof(this)(chosenI
, staticMap
!(saveOrInit
,
2001 aliasSeqOf
!(rs
.length
.iota
)));
2006 private enum overloadValidFor(alias r
) = is(typeof(r
.front
= T
.init
));
2008 static if (allSatisfy
!(overloadValidFor
, rs
))
2011 actOnChosen
!((ref r
, T v
) { r
.front
= v
; })(this, v
);
2015 static if (allSatisfy
!(hasMobileElements
, Ranges
))
2018 return actOnChosen
!((ref r
) => r
.moveFront
)(this);
2021 static if (allSatisfy
!(isBidirectionalRange
, Ranges
))
2023 @property auto ref back()
2025 static auto ref getBack(R
)(ref R r
) { return r
.back
; }
2026 return actOnChosen
!getBack(this);
2031 actOnChosen
!((ref r
) { r
.popBack
; })(this);
2034 static if (allSatisfy
!(hasMobileElements
, Ranges
))
2037 return actOnChosen
!((ref r
) => r
.moveBack
)(this);
2042 private enum overloadValidFor(alias r
) = is(typeof(r
.back
= T
.init
));
2044 static if (allSatisfy
!(overloadValidFor
, rs
))
2047 actOnChosen
!((ref r
, T v
) { r
.back
= v
; })(this, v
);
2052 static if (allSatisfy
!(hasLength
, Ranges
))
2054 @property size_t
length()
2056 return actOnChosen
!(r
=> r
.length
)(this);
2058 alias opDollar
= length
;
2061 static if (allSatisfy
!(isRandomAccessRange
, Ranges
))
2063 auto ref opIndex(size_t index
)
2065 static auto ref get(R
)(ref R r
, size_t index
) { return r
[index
]; }
2066 return actOnChosen
!get(this, index
);
2069 static if (allSatisfy
!(hasMobileElements
, Ranges
))
2070 auto moveAt(size_t index
)
2072 return actOnChosen
!((ref r
, size_t index
) => r
.moveAt(index
))
2076 private enum indexAssignable(T
, R
) = is(typeof(lvalueOf
!R
[1] = T
.init
));
2078 template opIndexAssign(T
)
2079 if (allSatisfy
!(ApplyLeft
!(indexAssignable
, T
), Ranges
))
2081 void opIndexAssign(T v
, size_t index
)
2083 return actOnChosen
!((ref r
, size_t index
, T v
) { r
[index
] = v
; })
2089 static if (allSatisfy
!(hasSlicing
, Ranges
))
2090 auto opSlice(size_t begin
, size_t end
)
2092 alias Slice(R
) = typeof(R
.init
[0 .. 1]);
2093 alias Slices
= staticMap
!(Slice
, Ranges
);
2095 auto sliceOrInit(size_t i
)()
2097 ref getI() @trusted { return rs
[i
]; }
2098 return i
== chosenI?
getI()[begin
.. end
]: Slices
[i
].init
;
2101 return chooseAmong(chosenI
, staticMap
!(sliceOrInit
,
2102 aliasSeqOf
!(rs
.length
.iota
)));
2106 // https://issues.dlang.org/show_bug.cgi?id=18657
2109 import std
.algorithm
.comparison
: equal
;
2111 auto r
= choose(true, refRange(&s
), "bar");
2112 assert(equal(r
.save
, "foo"));
2113 assert(equal(r
, "foo"));
2125 // `p = q;` is only there to prevent inference of `scope return`.
2126 @property @safe R
save() { p
= q
; return this; }
2130 choose(true, r
, r
).save
;
2133 // Make sure ChooseResult.save doesn't trust @system user code.
2134 @system unittest // copy is @system
2141 this(this) @system {}
2142 @property R
save() { return R(front
, empty
); }
2144 choose(true, R(), R()).save
;
2145 choose(true, [0], R()).save
;
2146 choose(true, R(), [0]).save
;
2149 @safe unittest // copy is @system
2156 this(this) @system {}
2157 @property R
save() { return R(front
, empty
); }
2159 static assert(!__traits(compiles
, choose(true, R(), R()).save
));
2160 static assert(!__traits(compiles
, choose(true, [0], R()).save
));
2161 static assert(!__traits(compiles
, choose(true, R(), [0]).save
));
2164 @system unittest // .save is @system
2171 @property R
save() @system { return this; }
2173 choose(true, R(), R()).save
;
2174 choose(true, [0], R()).save
;
2175 choose(true, R(), [0]).save
;
2178 @safe unittest // .save is @system
2185 @property R
save() @system { return this; }
2187 static assert(!__traits(compiles
, choose(true, R(), R()).save
));
2188 static assert(!__traits(compiles
, choose(true, [0], R()).save
));
2189 static assert(!__traits(compiles
, choose(true, R(), [0]).save
));
2192 //https://issues.dlang.org/show_bug.cgi?id=19738
2193 @safe nothrow pure @nogc unittest
2195 static struct EvilRange
2199 void popFront() @safe {}
2200 auto opAssign(const ref EvilRange other
)
2202 *(cast(uint*) 0xcafebabe) = 0xdeadbeef;
2207 static assert(!__traits(compiles
, () @safe
2209 auto c1
= choose(true, EvilRange(), EvilRange());
2216 // https://issues.dlang.org/show_bug.cgi?id=20495
2219 static struct KillableRange
2222 ref int front() { return *item
; }
2223 bool empty() { return *item
> 10; }
2224 void popFront() { ++(*item
); }
2227 assert(item
is null ||
cast(size_t
) item
> 1000);
2228 item
= new int(*item
);
2230 KillableRange
save() { return this; }
2233 auto kr
= KillableRange(new int(1));
2234 int[] x
= [1,2,3,4,5]; // length is first
2236 auto chosen
= choose(true, x
, kr
);
2237 auto chosen2
= chosen
.save
;
2240 pure @safe nothrow unittest
2244 @disable this(this);
2247 auto a
= [S(1), S(2), S(3)];
2248 auto b
= [S(4), S(5), S(6)];
2250 auto chosen
= choose(true, a
, b
);
2251 assert(chosen
.front
.v
== 1);
2253 auto chosen2
= choose(false, a
, b
);
2254 assert(chosen2
.front
.v
== 4);
2257 // https://issues.dlang.org/show_bug.cgi?id=15708
2260 static struct HasPostblit
2276 auto chosen
= choose(true, range
, arr
);
2281 Choose one of multiple ranges at runtime.
2283 The ranges may be different, but they must have compatible element types. The
2284 result is a range that offers the weakest capabilities of all `Ranges`.
2287 index = which range to choose, must be less than the number of ranges
2288 rs = two or more ranges
2291 The indexed range. If rs consists of only one range, the return type is an
2292 alias of that range's type.
2294 auto chooseAmong(Ranges
...)(size_t index
, return scope Ranges rs
)
2295 if (Ranges
.length
>= 2
2296 && allSatisfy
!(isInputRange
, staticMap
!(Unqual
, Ranges
))
2297 && !is(CommonType
!(staticMap
!(ElementType
, Ranges
)) == void))
2299 return ChooseResult
!Ranges(index
, rs
);
2303 @safe nothrow pure @nogc unittest
2307 import std
.algorithm
.comparison
: equal
;
2309 int[4] sarr1
= [1, 2, 3, 4];
2310 int[2] sarr2
= [5, 6];
2312 auto arr1
= sarr1
[];
2313 auto arr2
= sarr2
[];
2314 auto arr3
= sarr3
[];
2317 auto s
= chooseAmong(0, arr1
, arr2
, arr3
);
2319 assert(s
.length
== 4);
2322 assert(equal(t
, only(1, 2, 3, 4)));
2325 auto s
= chooseAmong(1, arr1
, arr2
, arr3
);
2326 assert(s
.length
== 2);
2328 assert(equal(s
, only(8, 6)));
2331 auto s
= chooseAmong(1, arr1
, arr2
, arr3
);
2332 assert(s
.length
== 2);
2334 assert(equal(s
, only(8, 9)));
2337 auto s
= chooseAmong(1, arr2
, arr1
, arr3
)[1 .. 3];
2338 assert(s
.length
== 2);
2339 assert(equal(s
, only(2, 3)));
2342 auto s
= chooseAmong(0, arr1
, arr2
, arr3
);
2343 assert(s
.length
== 4);
2344 assert(s
.back
== 4);
2347 assert(equal(s
, only(1, 2, 5)));
2349 assert(equal(s
, only(1, 2, 3)));
2352 uint[5] foo
= [1, 2, 3, 4, 5];
2353 uint[5] bar
= [6, 7, 8, 9, 10];
2354 auto c
= chooseAmong(1, foo
[], bar
[]);
2358 assert(c
.moveFront() == 6);
2359 assert(c
.moveBack() == 10);
2360 assert(c
.moveAt(4) == 10);
2363 import std
.range
: cycle
;
2364 auto s
= chooseAmong(0, cycle(arr2
), cycle(arr3
));
2365 assert(isInfinite
!(typeof(s
)));
2367 assert(s
[100] == 8);
2368 assert(s
[101] == 9);
2369 assert(s
[0 .. 3].equal(only(8, 9, 8)));
2375 // and at compile time
2379 @safe nothrow pure @nogc unittest
2381 int[3] a
= [1, 2, 3];
2382 long[3] b
= [4, 5, 6];
2383 auto c
= chooseAmong(0, a
[], b
[]);
2388 @safe nothrow pure @nogc unittest
2390 static struct RefAccessRange
2393 ref front() @property { return r
[0]; }
2394 ref back() @property { return r
[$ - 1]; }
2395 void popFront() { r
= r
[1 .. $]; }
2396 void popBack() { r
= r
[0 .. $ - 1]; }
2397 auto empty() @property { return r
.empty
; }
2398 ref opIndex(size_t i
) { return r
[i
]; }
2399 auto length() @property { return r
.length
; }
2400 alias opDollar
= length
;
2401 auto save() { return this; }
2403 static assert(isRandomAccessRange
!RefAccessRange
);
2404 static assert(isRandomAccessRange
!RefAccessRange
);
2405 int[4] a
= [4, 3, 2, 1];
2407 auto c
= chooseAmong(0, RefAccessRange(a
[]), RefAccessRange(b
[]));
2409 void refFunc(ref int a
, int target
) { assert(a
== target
); }
2412 refFunc(c
.front
, 4);
2418 $(D roundRobin(r1, r2, r3)) yields `r1.front`, then `r2.front`,
2419 then `r3.front`, after which it pops off one element from each and
2420 continues again from `r1`. For example, if two ranges are involved,
2421 it alternately yields elements off the two ranges. `roundRobin`
2422 stops after it has consumed all ranges (skipping over the ones that
2425 auto roundRobin(Rs
...)(Rs rs
)
2426 if (Rs
.length
> 1 && allSatisfy
!(isInputRange
, staticMap
!(Unqual
, Rs
)))
2430 import std
.conv
: to
;
2433 private size_t _current
= size_t
.max
;
2435 @property bool empty()
2437 foreach (i
, Unused
; Rs
)
2439 if (!source
[i
].empty
) return false;
2444 @property auto ref front()
2446 final switch (_current
)
2453 "Attempting to fetch the front of an empty roundRobin"
2455 return source
[i
].front
;
2463 final switch (_current
)
2468 source
[i
].popFront();
2473 auto next
= _current
== (Rs
.length
- 1) ?
0 : (_current
+ 1);
2479 if (!source
[i
].empty
)
2486 _current
= _current
.max
;
2489 goto case (i
+ 1) % Rs
.length
;
2494 static if (allSatisfy
!(isForwardRange
, staticMap
!(Unqual
, Rs
)))
2495 @property auto save()
2497 auto saveSource(size_t len
)()
2499 import std
.typecons
: tuple
;
2500 static assert(len
> 0);
2501 static if (len
== 1)
2503 return tuple(source
[0].save
);
2507 return saveSource
!(len
- 1)() ~
2508 tuple(source
[len
- 1].save
);
2511 return Result(saveSource
!(Rs
.length
).expand
, _current
);
2514 static if (allSatisfy
!(hasLength
, Rs
))
2516 @property size_t
length()
2521 result
+= source
[i
].length
;
2526 alias opDollar
= length
;
2530 size_t firstNonEmpty
= size_t
.max
;
2531 static foreach (i
; 0 .. Rs
.length
)
2533 if (firstNonEmpty
== size_t
.max
&& !rs
[i
].empty
)
2537 return Result(rs
, firstNonEmpty
);
2543 import std
.algorithm
.comparison
: equal
;
2545 int[] a
= [ 1, 2, 3 ];
2546 int[] b
= [ 10, 20, 30, 40 ];
2547 auto r
= roundRobin(a
, b
);
2548 assert(equal(r
, [ 1, 10, 2, 20, 3, 30, 40 ]));
2552 * roundRobin can be used to create "interleave" functionality which inserts
2553 * an element between each element in a range.
2557 import std
.algorithm
.comparison
: equal
;
2559 auto interleave(R
, E
)(R range
, E element
)
2560 if ((isInputRange
!R
&& hasLength
!R
) || isForwardRange
!R
)
2562 static if (hasLength
!R
)
2563 immutable len
= range
.length
;
2565 immutable len
= range
.save
.walkLength
;
2569 element
.repeat(len
- 1)
2573 assert(interleave([1, 2, 3], 0).equal([1, 0, 2, 0, 3]));
2578 import std
.algorithm
.comparison
: equal
;
2579 string f
= "foo", b
= "bar";
2580 auto r
= roundRobin(refRange(&f
), refRange(&b
));
2581 assert(equal(r
.save
, "fboaor"));
2582 assert(equal(r
.save
, "fboaor"));
2584 pure @safe nothrow unittest
2586 import std
.algorithm
.comparison
: equal
;
2590 @disable this(this);
2593 S
[] a
= [ S(1), S(2) ];
2594 S
[] b
= [ S(10), S(20) ];
2595 auto r
= roundRobin(a
, b
);
2596 assert(equal(r
, [ S(1), S(10), S(2), S(20) ]));
2599 // https://issues.dlang.org/show_bug.cgi?id=24384
2602 auto r
= roundRobin("", "a");
2608 Iterates a random-access range starting from a given point and
2609 progressively extending left and right from that point. If no initial
2610 point is given, iteration starts from the middle of the
2611 range. Iteration spans the entire range.
2613 When `startingIndex` is 0 the range will be fully iterated in order
2614 and in reverse order when `r.length` is given.
2617 r = a random access range with length and slicing
2618 startingIndex = the index to begin iteration from
2621 A forward range with length
2623 auto radial(Range
, I
)(Range r
, I startingIndex
)
2624 if (isRandomAccessRange
!(Unqual
!Range
) && hasLength
!(Unqual
!Range
) && hasSlicing
!(Unqual
!Range
) && isIntegral
!I
)
2626 if (startingIndex
!= r
.length
) ++startingIndex
;
2627 return roundRobin(retro(r
[0 .. startingIndex
]), r
[startingIndex
.. r
.length
]);
2632 if (isRandomAccessRange
!(Unqual
!R
) && hasLength
!(Unqual
!R
) && hasSlicing
!(Unqual
!R
))
2634 return .radial(r
, (r
.length
- !r
.empty
) / 2);
2640 import std
.algorithm
.comparison
: equal
;
2641 int[] a
= [ 1, 2, 3, 4, 5 ];
2642 assert(equal(radial(a
), [ 3, 4, 2, 5, 1 ]));
2644 assert(equal(radial(a
), [ 2, 3, 1, 4 ]));
2646 // If the left end is reached first, the remaining elements on the right
2647 // are concatenated in order:
2648 a
= [ 0, 1, 2, 3, 4, 5 ];
2649 assert(equal(radial(a
, 1), [ 1, 2, 0, 3, 4, 5 ]));
2651 // If the right end is reached first, the remaining elements on the left
2652 // are concatenated in reverse order:
2653 assert(equal(radial(a
, 4), [ 4, 5, 3, 2, 1, 0 ]));
2658 import std
.algorithm
.comparison
: equal
;
2659 import std
.conv
: text
;
2660 import std
.exception
: enforce
;
2661 import std
.internal
.test.dummyrange
: DummyRange
, Length
, RangeType
, ReturnBy
;
2663 void test(int[] input
, int[] witness
)
2665 enforce(equal(radial(input
), witness
),
2666 text(radial(input
), " vs. ", witness
));
2670 test([ 1, 2 ], [ 1, 2 ]);
2671 test([ 1, 2, 3 ], [ 2, 3, 1 ]);
2672 test([ 1, 2, 3, 4 ], [ 2, 3, 1, 4 ]);
2673 test([ 1, 2, 3, 4, 5 ], [ 3, 4, 2, 5, 1 ]);
2674 test([ 1, 2, 3, 4, 5, 6 ], [ 3, 4, 2, 5, 1, 6 ]);
2676 int[] a
= [ 1, 2, 3, 4, 5 ];
2677 assert(equal(radial(a
, 1), [ 2, 3, 1, 4, 5 ]));
2678 assert(equal(radial(a
, 0), [ 1, 2, 3, 4, 5 ])); // only right subrange
2679 assert(equal(radial(a
, a
.length
), [ 5, 4, 3, 2, 1 ])); // only left subrange
2680 static assert(isForwardRange
!(typeof(radial(a
, 1))));
2682 auto r
= radial([1,2,3,4,5]);
2683 for (auto rr
= r
.save
; !rr
.empty
; rr
.popFront())
2685 assert(rr
.front
== moveFront(rr
));
2688 assert(r
.front
== 5);
2690 // Test instantiation without lvalue elements.
2691 DummyRange
!(ReturnBy
.Value
, Length
.Yes
, RangeType
.Random
) dummy
;
2692 assert(equal(radial(dummy
, 4), [5, 6, 4, 7, 3, 8, 2, 9, 1, 10]));
2694 // immutable int[] immi = [ 1, 2 ];
2695 // static assert(is(typeof(radial(immi))));
2700 import std
.algorithm
.comparison
: equal
;
2702 auto LL
= iota(1L, 6L);
2703 auto r
= radial(LL
);
2704 assert(equal(r
, [3L, 4L, 2L, 5L, 1L]));
2708 Lazily takes only up to `n` elements of a range. This is
2709 particularly useful when using with infinite ranges.
2711 Unlike $(LREF takeExactly), `take` does not require that there
2712 are `n` or more elements in `input`. As a consequence, length
2713 information is not applied to the result unless `input` also has
2717 input = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2718 to iterate over up to `n` times
2719 n = the number of elements to take
2722 At minimum, an input range. If the range offers random access
2723 and `length`, `take` offers them as well.
2725 Take
!R
take(R
)(R input
, size_t n
)
2726 if (isInputRange
!(Unqual
!R
))
2729 static if (is(R T
== Take
!T
))
2731 import std
.algorithm
.comparison
: min
;
2732 return R(input
.source
, min(n
, input
._maxAvailable
));
2734 else static if (!isInfinite
!U
&& hasSlicing
!U
)
2736 import std
.algorithm
.comparison
: min
;
2737 return input
[0 .. min(n
, input
.length
)];
2741 return Take
!R(input
, n
);
2747 if (isInputRange
!(Unqual
!Range
) &&
2748 //take _cannot_ test hasSlicing on infinite ranges, because hasSlicing uses
2749 //take for slicing infinite ranges.
2750 !((!isInfinite
!(Unqual
!Range
) && hasSlicing
!(Unqual
!Range
)) ||
is(Range T
== Take
!T
)))
2752 private alias R
= Unqual
!Range
;
2754 /// User accessible in read and write
2757 private size_t _maxAvailable
;
2761 /// Range primitives
2762 @property bool empty()
2764 return _maxAvailable
== 0 || source
.empty
;
2768 @property auto ref front()
2771 "Attempting to fetch the front of an empty "
2773 return source
.front
;
2780 "Attempting to popFront() past the end of a "
2786 static if (isForwardRange
!R
)
2788 @property Take
save()
2790 return Take(source
.save
, _maxAvailable
);
2793 static if (hasAssignableElements
!R
)
2795 @property void front(ElementType
!R v
)
2797 import core
.lifetime
: forward
;
2800 "Attempting to assign to the front of an empty "
2803 // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542
2804 source
.front
= __ctfe ? v
: forward
!v
;
2807 static if (hasMobileElements
!R
)
2813 "Attempting to move the front of an empty "
2815 return source
.moveFront();
2819 static if (isInfinite
!R
)
2822 @property size_t
length() const
2824 return _maxAvailable
;
2828 alias opDollar
= length
;
2830 //Note: Due to Take/hasSlicing circular dependency,
2831 //This needs to be a restrained template.
2833 auto opSlice()(size_t i
, size_t j
)
2836 assert(i
<= j
, "Invalid slice bounds");
2837 assert(j
<= length
, "Attempting to slice past the end of a "
2839 return source
[i
.. j
];
2842 else static if (hasLength
!R
)
2845 @property size_t
length()
2847 import std
.algorithm
.comparison
: min
;
2848 return min(_maxAvailable
, source
.length
);
2851 alias opDollar
= length
;
2854 static if (isRandomAccessRange
!R
)
2860 "Attempting to popBack() past the beginning of a "
2866 @property auto ref back()
2869 "Attempting to fetch the back of an empty "
2871 return source
[this.length
- 1];
2875 auto ref opIndex(size_t index
)
2877 assert(index
< length
,
2878 "Attempting to index out of the bounds of a "
2880 return source
[index
];
2883 static if (hasAssignableElements
!R
)
2886 @property void back(ElementType
!R v
)
2888 // This has to return auto instead of void because of
2889 // https://issues.dlang.org/show_bug.cgi?id=4706
2891 "Attempting to assign to the back of an empty "
2893 source
[this.length
- 1] = v
;
2897 void opIndexAssign(ElementType
!R v
, size_t index
)
2899 assert(index
< length
,
2900 "Attempting to index out of the bounds of a "
2906 static if (hasMobileElements
!R
)
2912 "Attempting to move the back of an empty "
2914 return source
.moveAt(this.length
- 1);
2918 auto moveAt(size_t index
)
2920 assert(index
< length
,
2921 "Attempting to index out of the bounds of a "
2923 return source
.moveAt(index
);
2929 Access to maximal length of the range.
2930 Note: the actual length of the range depends on the underlying range.
2931 If it has fewer elements, it will stop before maxLength is reached.
2933 @property size_t
maxLength() const
2935 return _maxAvailable
;
2941 if (isInputRange
!(Unqual
!R
) &&
2942 ((!isInfinite
!(Unqual
!R
) && hasSlicing
!(Unqual
!R
)) ||
is(R T
== Take
!T
)))
2948 pure @safe nothrow unittest
2950 import std
.algorithm
.comparison
: equal
;
2952 int[] arr1
= [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
2953 auto s
= take(arr1
, 5);
2954 assert(s
.length
== 5);
2956 assert(equal(s
, [ 1, 2, 3, 4, 5 ][]));
2960 * If the range runs out before `n` elements, `take` simply returns the entire
2961 * range (unlike $(LREF takeExactly), which will cause an assertion failure if
2962 * the range ends prematurely):
2964 pure @safe nothrow unittest
2966 import std
.algorithm
.comparison
: equal
;
2968 int[] arr2
= [ 1, 2, 3 ];
2969 auto t
= take(arr2
, 5);
2970 assert(t
.length
== 3);
2971 assert(equal(t
, [ 1, 2, 3 ]));
2974 pure @safe nothrow unittest
2976 import std
.algorithm
.comparison
: equal
;
2977 import std
.internal
.test.dummyrange
: AllDummyRanges
;
2979 int[] arr1
= [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
2980 auto s
= take(arr1
, 5);
2981 assert(s
.length
== 5);
2983 assert(equal(s
, [ 1, 2, 3, 4, 5 ][]));
2984 assert(equal(retro(s
), [ 5, 4, 3, 2, 1 ][]));
2986 // Test fix for bug 4464.
2987 static assert(is(typeof(s
) == Take
!(int[])));
2988 static assert(is(typeof(s
) == int[]));
2990 // Test using narrow strings.
2991 import std
.exception
: assumeWontThrow
;
2993 auto myStr
= "This is a string.";
2994 auto takeMyStr
= take(myStr
, 7);
2995 assert(assumeWontThrow(equal(takeMyStr
, "This is")));
2996 // Test fix for bug 5052.
2997 auto takeMyStrAgain
= take(takeMyStr
, 4);
2998 assert(assumeWontThrow(equal(takeMyStrAgain
, "This")));
2999 static assert(is (typeof(takeMyStrAgain
) == typeof(takeMyStr
)));
3000 takeMyStrAgain
= take(takeMyStr
, 10);
3001 assert(assumeWontThrow(equal(takeMyStrAgain
, "This is")));
3003 foreach (DummyType
; AllDummyRanges
)
3006 auto t
= take(dummy
, 5);
3007 alias T
= typeof(t
);
3009 static if (isRandomAccessRange
!DummyType
)
3011 static assert(isRandomAccessRange
!T
);
3014 assert(moveAt(t
, 1) == t
[1]);
3015 assert(t
.back
== moveBack(t
));
3017 else static if (isForwardRange
!DummyType
)
3019 static assert(isForwardRange
!T
);
3022 for (auto tt
= t
; !tt
.empty
; tt
.popFront())
3024 assert(tt
.front
== moveFront(tt
));
3027 // Bidirectional ranges can't be propagated properly if they don't
3028 // also have random access.
3030 assert(equal(t
, [1,2,3,4,5]));
3032 //Test that take doesn't wrap the result of take.
3033 assert(take(t
, 4) == take(dummy
, 4));
3036 immutable myRepeat
= repeat(1);
3037 static assert(is(Take
!(typeof(myRepeat
))));
3040 pure @safe nothrow @nogc unittest
3042 //check for correct slicing of Take on an infinite range
3043 import std
.algorithm
.comparison
: equal
;
3044 foreach (start
; 0 .. 4)
3045 foreach (stop
; start
.. 4)
3046 assert(iota(4).cycle
.take(4)[start
.. stop
]
3047 .equal(iota(start
, stop
)));
3050 pure @safe nothrow @nogc unittest
3052 // Check that one can declare variables of all Take types,
3053 // and that they match the return type of the corresponding
3055 // See https://issues.dlang.org/show_bug.cgi?id=4464
3066 Take
!(Take
!string
) t3
;
3071 pure @safe nothrow @nogc unittest
3073 alias R1
= typeof(repeat(1));
3074 alias R2
= typeof(cycle([1]));
3075 alias TR1
= Take
!R1
;
3076 alias TR2
= Take
!R2
;
3077 static assert(isBidirectionalRange
!TR1
);
3078 static assert(isBidirectionalRange
!TR2
);
3081 // https://issues.dlang.org/show_bug.cgi?id=12731
3082 pure @safe nothrow @nogc unittest
3087 assert(s
.length
== 2);
3092 // https://issues.dlang.org/show_bug.cgi?id=13151
3093 pure @safe nothrow @nogc unittest
3095 import std
.algorithm
.comparison
: equal
;
3097 auto r
= take(repeat(1, 4), 3);
3098 assert(r
.take(2).equal(repeat(1, 2)));
3101 // https://issues.dlang.org/show_bug.cgi?id=24481
3104 import std
.algorithm
.iteration
: filter
;
3110 void opAssign()(auto ref const(typeof(this)) that
) const { called
= true; }
3113 const(Handle
)[5] arr
= [Handle(0), Handle(1), Handle(2), Handle(3), Handle(4)];
3114 auto range
= arr
[].filter
!(a
=> true)().take(3);
3117 range
.front
= Handle(42);
3122 Similar to $(LREF take), but assumes that `range` has at least $(D
3123 n) elements. Consequently, the result of $(D takeExactly(range, n))
3124 always defines the `length` property (and initializes it to `n`)
3125 even when `range` itself does not define `length`.
3127 The result of `takeExactly` is identical to that of $(LREF take) in
3128 cases where the original range defines `length` or is infinite.
3130 Unlike $(LREF take), however, it is illegal to pass a range with less than
3131 `n` elements to `takeExactly`; this will cause an assertion failure.
3133 auto takeExactly(R
)(R range
, size_t n
)
3136 static if (is(typeof(takeExactly(range
._input
, n
)) == R
))
3138 assert(n
<= range
._n
,
3139 "Attempted to take more than the length of the range with takeExactly.");
3140 // takeExactly(takeExactly(r, n1), n2) has the same type as
3141 // takeExactly(r, n1) and simply returns takeExactly(r, n2)
3145 //Also covers hasSlicing!R for finite ranges.
3146 else static if (hasLength
!R
)
3148 assert(n
<= range
.length
,
3149 "Attempted to take more than the length of the range with takeExactly.");
3150 return take(range
, n
);
3152 else static if (isInfinite
!R
)
3153 return Take
!R(range
, n
);
3156 static struct Result
3161 @property bool empty() const { return !_n
; }
3162 @property auto ref front()
3164 assert(_n
> 0, "front() on an empty " ~ Result
.stringof
);
3165 return _input
.front
;
3167 void popFront() { _input
.popFront(); --_n
; }
3168 @property size_t
length() const { return _n
; }
3169 alias opDollar
= length
;
3171 @property auto _takeExactly_Result_asTake()
3173 return take(_input
, _n
);
3176 alias _takeExactly_Result_asTake
this;
3178 static if (isForwardRange
!R
)
3179 @property auto save()
3181 return Result(_input
.save
, _n
);
3184 static if (hasMobileElements
!R
)
3189 "Attempting to move the front of an empty "
3190 ~ typeof(this).stringof
);
3191 return _input
.moveFront();
3195 static if (hasAssignableElements
!R
)
3197 @property auto ref front(ElementType
!R v
)
3199 import core
.lifetime
: forward
;
3202 "Attempting to assign to the front of an empty "
3203 ~ typeof(this).stringof
);
3205 // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542
3206 return _input
.front
= __ctfe ? v
: forward
!v
;
3211 return Result(range
, n
);
3216 pure @safe nothrow unittest
3218 import std
.algorithm
.comparison
: equal
;
3220 auto a
= [ 1, 2, 3, 4, 5 ];
3222 auto b
= takeExactly(a
, 3);
3223 assert(equal(b
, [1, 2, 3]));
3224 static assert(is(typeof(b
.length
) == size_t
));
3225 assert(b
.length
== 3);
3226 assert(b
.front
== 1);
3227 assert(b
.back
== 3);
3230 pure @safe nothrow unittest
3232 import std
.algorithm
.comparison
: equal
;
3233 import std
.algorithm
.iteration
: filter
;
3235 auto a
= [ 1, 2, 3, 4, 5 ];
3236 auto b
= takeExactly(a
, 3);
3237 assert(equal(b
, [1, 2, 3]));
3238 auto c
= takeExactly(b
, 2);
3239 assert(equal(c
, [1, 2]));
3243 auto d
= filter
!"a > 2"(a
);
3244 auto e
= takeExactly(d
, 3);
3245 assert(equal(e
, [3, 4, 5]));
3246 static assert(is(typeof(e
.length
) == size_t
));
3247 assert(e
.length
== 3);
3248 assert(e
.front
== 3);
3250 assert(equal(takeExactly(e
, 3), [3, 4, 5]));
3253 pure @safe nothrow unittest
3255 import std
.algorithm
.comparison
: equal
;
3256 import std
.internal
.test.dummyrange
: AllDummyRanges
;
3258 auto a
= [ 1, 2, 3, 4, 5 ];
3259 //Test that take and takeExactly are the same for ranges which define length
3260 //but aren't sliceable.
3263 @property auto front() { return _arr
[0]; }
3264 @property bool empty() { return _arr
.empty
; }
3265 void popFront() { _arr
.popFront(); }
3266 @property size_t
length() { return _arr
.length
; }
3269 static assert(is(typeof(take(L(a
), 3)) == typeof(takeExactly(L(a
), 3))));
3270 assert(take(L(a
), 3) == takeExactly(L(a
), 3));
3272 //Test that take and takeExactly are the same for ranges which are sliceable.
3273 static assert(is(typeof(take(a
, 3)) == typeof(takeExactly(a
, 3))));
3274 assert(take(a
, 3) == takeExactly(a
, 3));
3276 //Test that take and takeExactly are the same for infinite ranges.
3277 auto inf
= repeat(1);
3278 static assert(is(typeof(take(inf
, 5)) == Take
!(typeof(inf
))));
3279 assert(take(inf
, 5) == takeExactly(inf
, 5));
3281 //Test that take and takeExactly are _not_ the same for ranges which don't
3283 static assert(!is(typeof(take(filter
!"true"(a
), 3)) == typeof(takeExactly(filter
!"true"(a
), 3))));
3285 foreach (DummyType
; AllDummyRanges
)
3289 auto t
= takeExactly(dummy
, 5);
3291 //Test that takeExactly doesn't wrap the result of takeExactly.
3292 assert(takeExactly(t
, 4) == takeExactly(dummy
, 4));
3295 static if (hasMobileElements
!DummyType
)
3298 auto t
= takeExactly(DummyType
.init
, 4);
3299 assert(t
.moveFront() == 1);
3300 assert(equal(t
, [1, 2, 3, 4]));
3304 static if (hasAssignableElements
!DummyType
)
3307 auto t
= takeExactly(DummyType
.init
, 4);
3309 assert(equal(t
, [9, 2, 3, 4]));
3315 pure @safe nothrow unittest
3317 import std
.algorithm
.comparison
: equal
;
3318 import std
.internal
.test.dummyrange
: DummyRange
, Length
, RangeType
, ReturnBy
;
3320 alias DummyType
= DummyRange
!(ReturnBy
.Value
, Length
.No
, RangeType
.Forward
);
3321 auto te
= takeExactly(DummyType(), 5);
3322 Take
!DummyType t
= te
;
3323 assert(equal(t
, [1, 2, 3, 4, 5]));
3324 assert(equal(t
, te
));
3327 // https://issues.dlang.org/show_bug.cgi?id=18092
3328 // can't combine take and takeExactly
3331 import std
.algorithm
.comparison
: equal
;
3332 import std
.internal
.test.dummyrange
: AllDummyRanges
;
3334 static foreach (Range
; AllDummyRanges
)
3337 assert(r
.take(6).takeExactly(2).equal([1, 2]));
3338 assert(r
.takeExactly(6).takeExactly(2).equal([1, 2]));
3339 assert(r
.takeExactly(6).take(2).equal([1, 2]));
3343 // https://issues.dlang.org/show_bug.cgi?id=24481
3346 import std
.algorithm
.iteration
: filter
;
3352 void opAssign()(auto ref const(typeof(this)) that
) const { called
= true; }
3355 const(Handle
)[5] arr
= [Handle(0), Handle(1), Handle(2), Handle(3), Handle(4)];
3356 auto range
= arr
[].filter
!(a
=> true)().takeExactly(3);
3359 range
.front
= Handle(42);
3364 Returns a range with at most one element; for example, $(D
3365 takeOne([42, 43, 44])) returns a range consisting of the integer $(D
3366 42). Calling `popFront()` off that range renders it empty.
3368 In effect `takeOne(r)` is somewhat equivalent to $(D take(r, 1)) but in
3369 certain interfaces it is important to know statically that the range may only
3370 have at most one element.
3372 The type returned by `takeOne` is a random-access range with length
3373 regardless of `R`'s capabilities, as long as it is a forward range.
3374 (another feature that distinguishes `takeOne` from `take`). If
3375 (D R) is an input range but not a forward range, return type is an input
3376 range with all random-access capabilities except save.
3378 auto takeOne(R
)(R source
)
3381 static if (hasSlicing
!R
)
3383 return source
[0 .. !source
.empty
];
3387 static struct Result
3390 private bool _empty
= true;
3391 @property bool empty() const { return _empty
; }
3392 @property auto ref front()
3394 assert(!empty
, "Attempting to fetch the front of an empty takeOne");
3395 return _source
.front
;
3399 assert(!empty
, "Attempting to popFront an empty takeOne");
3405 assert(!empty
, "Attempting to popBack an empty takeOne");
3409 static if (isForwardRange
!(Unqual
!R
))
3411 @property auto save() { return Result(_source
.save
, empty
); }
3413 @property auto ref back()
3415 assert(!empty
, "Attempting to fetch the back of an empty takeOne");
3416 return _source
.front
;
3418 @property size_t
length() const { return !empty
; }
3419 alias opDollar
= length
;
3420 auto ref opIndex(size_t n
)
3422 assert(n
< length
, "Attempting to index a takeOne out of bounds");
3423 return _source
.front
;
3425 auto opSlice(size_t m
, size_t n
)
3429 "Attempting to slice a takeOne range with a larger first argument than the second."
3433 "Attempting to slice using an out of bounds index on a takeOne range."
3435 return n
> m ?
this : Result(_source
, true);
3437 // Non-standard property
3438 @property R
source() { return _source
; }
3441 return Result(source
, source
.empty
);
3446 pure @safe nothrow unittest
3448 auto s
= takeOne([42, 43, 44]);
3449 static assert(isRandomAccessRange
!(typeof(s
)));
3450 assert(s
.length
== 1);
3452 assert(s
.front
== 42);
3454 assert(s
.front
== 43);
3455 assert(s
.back
== 43);
3458 assert(s
.length
== 0);
3462 pure @safe nothrow @nogc unittest
3464 struct NonForwardRange
3467 int front() { return 42; }
3471 static assert(!isForwardRange
!NonForwardRange
);
3473 auto s
= takeOne(NonForwardRange());
3474 assert(s
.length
== 1);
3476 assert(s
.front
== 42);
3477 assert(s
.back
== 42);
3482 assert(t
.length
== 0);
3486 assert(u
.length
== 0);
3490 assert(s
.length
== 0);
3493 assert(v
.front
== 42);
3496 assert(v
.length
== 0);
3499 pure @safe nothrow @nogc unittest
3501 struct NonSlicingForwardRange
3504 int front() { return 42; }
3506 @property auto save() { return this; }
3509 static assert(isForwardRange
!NonSlicingForwardRange
);
3510 static assert(!hasSlicing
!NonSlicingForwardRange
);
3512 auto s
= takeOne(NonSlicingForwardRange());
3513 assert(s
.length
== 1);
3515 assert(s
.front
== 42);
3516 assert(s
.back
== 42);
3520 assert(s
.length
== 0);
3523 assert(t
.front
== 42);
3526 assert(t
.length
== 0);
3529 // Test that asserts trigger correctly
3532 import std
.exception
: assertThrown
;
3533 import core
.exception
: AssertError
;
3535 struct NonForwardRange
3538 int front() { return 42; }
3542 auto s
= takeOne(NonForwardRange());
3544 assertThrown
!AssertError(s
[1]);
3545 assertThrown
!AssertError(s
[0 .. 2]);
3547 size_t one
= 1; // Avoid style warnings triggered by literals
3549 assertThrown
!AssertError(s
[one
.. zero
]);
3553 assertThrown
!AssertError(s
.front
);
3554 assertThrown
!AssertError(s
.back
);
3555 assertThrown
!AssertError(s
.popFront
);
3556 assertThrown
!AssertError(s
.popBack
);
3559 // https://issues.dlang.org/show_bug.cgi?id=16999
3562 auto myIota
= new class
3565 @safe void popFront(){front
++;}
3568 auto iotaPart
= myIota
.takeOne
;
3570 foreach (var
; chain(iotaPart
, iotaPart
, iotaPart
))
3575 assert(iotaPart
.front
== 3);
3579 Returns an empty range which is statically known to be empty and is
3580 guaranteed to have `length` and be random access regardless of `R`'s
3586 return typeof(takeOne(R
.init
)).init
;
3590 pure @safe nothrow @nogc unittest
3592 auto range
= takeNone
!(int[])();
3593 assert(range
.length
== 0);
3594 assert(range
.empty
);
3597 pure @safe nothrow @nogc unittest
3599 enum ctfe
= takeNone
!(int[])();
3600 static assert(ctfe
.length
== 0);
3601 static assert(ctfe
.empty
);
3606 Creates an empty range from the given range in $(BIGOH 1). If it can, it
3607 will return the same range type. If not, it will return
3608 $(D takeExactly(range, 0)).
3610 auto takeNone(R
)(R range
)
3613 import std
.traits
: isDynamicArray
;
3614 //Makes it so that calls to takeNone which don't use UFCS still work with a
3615 //member version if it's defined.
3616 static if (is(typeof(R
.takeNone
)))
3617 auto retval
= range
.takeNone();
3618 // https://issues.dlang.org/show_bug.cgi?id=8339
3619 else static if (isDynamicArray
!R
)/+ ||
3620 (is(R == struct) && __traits(compiles, {auto r = R.init;}) && R.init.empty))+/
3622 auto retval
= R
.init
;
3624 //An infinite range sliced at [0 .. 0] would likely still not be empty...
3625 else static if (hasSlicing
!R
&& !isInfinite
!R
)
3626 auto retval
= range
[0 .. 0];
3628 auto retval
= takeExactly(range
, 0);
3630 // https://issues.dlang.org/show_bug.cgi?id=7892 prevents this from being
3631 // done in an out block.
3632 assert(retval
.empty
);
3637 pure @safe nothrow unittest
3639 import std
.algorithm
.iteration
: filter
;
3640 assert(takeNone([42, 27, 19]).empty
);
3641 assert(takeNone("dlang.org").empty
);
3642 assert(takeNone(filter
!"true"([42, 27, 19])).empty
);
3647 import std
.algorithm
.iteration
: filter
;
3648 import std
.meta
: AliasSeq
;
3652 mixin template genInput()
3655 @property bool empty() { return _arr
.empty
; }
3656 @property auto front() { return _arr
.front
; }
3657 void popFront() { _arr
.popFront(); }
3658 static assert(isInputRange
!(typeof(this)));
3661 alias genInput
= Dummy
.genInput
;
3663 static struct NormalStruct
3665 //Disabled to make sure that the takeExactly version is used.
3667 this(int[] arr
) { _arr
= arr
; }
3672 static struct SliceStruct
3675 this(int[] arr
) { _arr
= arr
; }
3677 @property auto save() { return this; }
3678 auto opSlice(size_t i
, size_t j
) { return typeof(this)(_arr
[i
.. j
]); }
3679 @property size_t
length() { return _arr
.length
; }
3683 static struct InitStruct
3689 static struct TakeNoneStruct
3691 this(int[] arr
) { _arr
= arr
; }
3694 auto takeNone() { return typeof(this)(null); }
3698 static class NormalClass
3700 this(int[] arr
) {_arr
= arr
;}
3705 static class SliceClass
3708 this(int[] arr
) { _arr
= arr
; }
3710 @property auto save() { return new typeof(this)(_arr
); }
3711 auto opSlice(size_t i
, size_t j
) { return new typeof(this)(_arr
[i
.. j
]); }
3712 @property size_t
length() { return _arr
.length
; }
3716 static class TakeNoneClass
3719 this(int[] arr
) { _arr
= arr
; }
3721 auto takeNone() { return new typeof(this)(null); }
3725 import std
.format
: format
;
3727 static foreach (range
; AliasSeq
!([1, 2, 3, 4, 5],
3731 SliceStruct([1, 2, 3]),
3732 // https://issues.dlang.org/show_bug.cgi?id=8339
3733 // forces this to be takeExactly `InitStruct([1, 2, 3]),
3734 TakeNoneStruct([1, 2, 3])))
3736 static assert(takeNone(range
).empty
, typeof(range
).stringof
);
3737 assert(takeNone(range
).empty
);
3738 static assert(is(typeof(range
) == typeof(takeNone(range
))), typeof(range
).stringof
);
3741 static foreach (range
; AliasSeq
!(NormalStruct([1, 2, 3]),
3742 InitStruct([1, 2, 3])))
3744 static assert(takeNone(range
).empty
, typeof(range
).stringof
);
3745 assert(takeNone(range
).empty
);
3746 static assert(is(typeof(takeExactly(range
, 0)) == typeof(takeNone(range
))), typeof(range
).stringof
);
3749 //Don't work in CTFE.
3750 auto normal
= new NormalClass([1, 2, 3]);
3751 assert(takeNone(normal
).empty
);
3752 static assert(is(typeof(takeExactly(normal
, 0)) == typeof(takeNone(normal
))), typeof(normal
).stringof
);
3754 auto slice
= new SliceClass([1, 2, 3]);
3755 assert(takeNone(slice
).empty
);
3756 static assert(is(SliceClass
== typeof(takeNone(slice
))), typeof(slice
).stringof
);
3758 auto taken
= new TakeNoneClass([1, 2, 3]);
3759 assert(takeNone(taken
).empty
);
3760 static assert(is(TakeNoneClass
== typeof(takeNone(taken
))), typeof(taken
).stringof
);
3762 auto filtered
= filter
!"true"([1, 2, 3, 4, 5]);
3763 assert(takeNone(filtered
).empty
);
3764 // https://issues.dlang.org/show_bug.cgi?id=8339 and
3765 // https://issues.dlang.org/show_bug.cgi?id=5941 force this to be takeExactly
3766 //static assert(is(typeof(filtered) == typeof(takeNone(filtered))), typeof(filtered).stringof);
3770 + Return a range advanced to within `_n` elements of the end of
3773 + Intended as the range equivalent of the Unix
3774 + $(HTTP en.wikipedia.org/wiki/Tail_%28Unix%29, _tail) utility. When the length
3775 + of `range` is less than or equal to `_n`, `range` is returned
3778 + Completes in $(BIGOH 1) steps for ranges that support slicing and have
3779 + length. Completes in $(BIGOH range.length) time for all other ranges.
3782 + range = range to get _tail of
3783 + n = maximum number of elements to include in _tail
3786 + Returns the _tail of `range` augmented with length information
3788 auto tail(Range
)(Range range
, size_t n
)
3789 if (isInputRange
!Range
&& !isInfinite
!Range
&&
3790 (hasLength
!Range || isForwardRange
!Range
))
3792 static if (hasLength
!Range
)
3794 immutable length
= range
.length
;
3796 return range
.takeExactly(length
);
3798 return range
.drop(length
- n
).takeExactly(n
);
3802 Range scout
= range
.save
;
3803 foreach (immutable i
; 0 .. n
)
3806 return range
.takeExactly(i
);
3810 auto tail
= range
.save
;
3811 while (!scout
.empty
)
3813 assert(!tail
.empty
);
3818 return tail
.takeExactly(n
);
3823 pure @safe nothrow unittest
3826 assert([1, 2, 3].tail(1) == [3]);
3827 assert([1, 2, 3].tail(2) == [2, 3]);
3828 assert([1, 2, 3].tail(3) == [1, 2, 3]);
3829 assert([1, 2, 3].tail(4) == [1, 2, 3]);
3830 assert([1, 2, 3].tail(0).length
== 0);
3833 import std
.algorithm
.comparison
: equal
;
3834 import std
.algorithm
.iteration
: joiner
;
3835 import std
.exception
: assumeWontThrow
;
3836 import std
.string
: lineSplitter
;
3837 assert("one\ntwo\nthree"
3841 .equal("two\nthree")
3845 // @nogc prevented by https://issues.dlang.org/show_bug.cgi?id=15408
3846 pure nothrow @safe /+@nogc+/ unittest
3848 import std
.algorithm
.comparison
: equal
;
3849 import std
.internal
.test.dummyrange
: AllDummyRanges
, DummyRange
, Length
,
3850 RangeType
, ReturnBy
;
3852 static immutable cheatsheet
= [6, 7, 8, 9, 10];
3854 foreach (R
; AllDummyRanges
)
3856 static if (isInputRange
!R
&& !isInfinite
!R
&&
3857 (hasLength
!R || isForwardRange
!R
))
3859 assert(R
.init
.tail(5).equal(cheatsheet
));
3860 static assert(R
.init
.tail(5).equal(cheatsheet
));
3862 assert(R
.init
.tail(0).length
== 0);
3863 assert(R
.init
.tail(10).equal(R
.init
));
3864 assert(R
.init
.tail(11).equal(R
.init
));
3868 // Infinite ranges are not supported
3869 static assert(!__traits(compiles
, repeat(0).tail(0)));
3871 // Neither are non-forward ranges without length
3872 static assert(!__traits(compiles
, DummyRange
!(ReturnBy
.Value
, Length
.No
,
3873 RangeType
.Input
).init
.tail(5)));
3876 pure @safe nothrow @nogc unittest
3878 static immutable input
= [1, 2, 3];
3879 static immutable expectedOutput
= [2, 3];
3880 assert(input
.tail(2) == expectedOutput
);
3884 Convenience function which calls
3885 $(REF popFrontN, std, range, primitives)`(range, n)` and returns `range`.
3886 `drop` makes it easier to pop elements from a range
3887 and then pass it to another function within a single expression,
3888 whereas `popFrontN` would require multiple statements.
3890 `dropBack` provides the same functionality but instead calls
3891 $(REF popBackN, std, range, primitives)`(range, n)`
3893 Note: `drop` and `dropBack` will only pop $(I up to)
3894 `n` elements but will stop if the range is empty first.
3895 In other languages this is sometimes called `skip`.
3898 range = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to drop from
3899 n = the number of elements to drop
3902 `range` with up to `n` elements dropped
3905 $(REF popFront, std, range, primitives), $(REF popBackN, std, range, primitives)
3907 R
drop(R
)(R range
, size_t n
)
3917 import std
.algorithm
.comparison
: equal
;
3919 assert([0, 2, 1, 5, 0, 3].drop(3) == [5, 0, 3]);
3920 assert("hello world".drop(6) == "world");
3921 assert("hello world".drop(50).empty
);
3922 assert("hello world".take(6).drop(3).equal("lo "));
3926 R
dropBack(R
)(R range
, size_t n
)
3927 if (isBidirectionalRange
!R
)
3936 import std
.algorithm
.comparison
: equal
;
3938 assert([0, 2, 1, 5, 0, 3].dropBack(3) == [0, 2, 1]);
3939 assert("hello world".dropBack(6) == "hello");
3940 assert("hello world".dropBack(50).empty
);
3941 assert("hello world".drop(4).dropBack(4).equal("o w"));
3946 import std
.algorithm
.comparison
: equal
;
3947 import std
.container
.dlist
: DList
;
3949 //Remove all but the first two elements
3950 auto a
= DList
!int(0, 1, 9, 9, 9, 9);
3951 a
.remove(a
[].drop(2));
3952 assert(a
[].equal(a
[].take(2)));
3957 import std
.algorithm
.comparison
: equal
;
3958 import std
.algorithm
.iteration
: filter
;
3960 assert(drop("", 5).empty
);
3961 assert(equal(drop(filter
!"true"([0, 2, 1, 5, 0, 3]), 3), [5, 0, 3]));
3966 import std
.algorithm
.comparison
: equal
;
3967 import std
.container
.dlist
: DList
;
3969 //insert before the last two elements
3970 auto a
= DList
!int(0, 1, 2, 5, 6);
3971 a
.insertAfter(a
[].dropBack(2), [3, 4]);
3972 assert(a
[].equal(iota(0, 7)));
3976 Similar to $(LREF drop) and `dropBack` but they call
3977 $(D range.$(LREF popFrontExactly)(n)) and `range.popBackExactly(n)`
3980 Note: Unlike `drop`, `dropExactly` will assume that the
3981 range holds at least `n` elements. This makes `dropExactly`
3982 faster than `drop`, but it also means that if `range` does
3983 not contain at least `n` elements, it will attempt to call `popFront`
3984 on an empty range, which is undefined behavior. So, only use
3985 `popFrontExactly` when it is guaranteed that `range` holds at least
3989 range = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to drop from
3990 n = the number of elements to drop
3993 `range` with `n` elements dropped
3996 $(REF popFrontExcatly, std, range, primitives),
3997 $(REF popBackExcatly, std, range, primitives)
3999 R
dropExactly(R
)(R range
, size_t n
)
4002 popFrontExactly(range
, n
);
4006 R
dropBackExactly(R
)(R range
, size_t n
)
4007 if (isBidirectionalRange
!R
)
4009 popBackExactly(range
, n
);
4016 import std
.algorithm
.comparison
: equal
;
4017 import std
.algorithm
.iteration
: filterBidirectional
;
4020 assert(a
.dropExactly(2) == [3]);
4021 assert(a
.dropBackExactly(2) == [1]);
4023 string s
= "日本語";
4024 assert(s
.dropExactly(2) == "語");
4025 assert(s
.dropBackExactly(2) == "æ—¥");
4027 auto bd
= filterBidirectional
!"true"([1, 2, 3]);
4028 assert(bd
.dropExactly(2).equal([3]));
4029 assert(bd
.dropBackExactly(2).equal([1]));
4033 Convenience function which calls
4034 `range.popFront()` and returns `range`. `dropOne`
4035 makes it easier to pop an element from a range
4036 and then pass it to another function within a single expression,
4037 whereas `popFront` would require multiple statements.
4039 `dropBackOne` provides the same functionality but instead calls
4042 R
dropOne(R
)(R range
)
4049 R
dropBackOne(R
)(R range
)
4050 if (isBidirectionalRange
!R
)
4057 pure @safe nothrow unittest
4059 import std
.algorithm
.comparison
: equal
;
4060 import std
.algorithm
.iteration
: filterBidirectional
;
4061 import std
.container
.dlist
: DList
;
4063 auto dl = DList
!int(9, 1, 2, 3, 9);
4064 assert(dl[].dropOne().dropBackOne().equal([1, 2, 3]));
4067 assert(a
.dropOne() == [2, 3]);
4068 assert(a
.dropBackOne() == [1, 2]);
4070 string s
= "日本語";
4071 import std
.exception
: assumeWontThrow
;
4072 assert(assumeWontThrow(s
.dropOne() == "本語"));
4073 assert(assumeWontThrow(s
.dropBackOne() == "日本"));
4075 auto bd
= filterBidirectional
!"true"([1, 2, 3]);
4076 assert(bd
.dropOne().equal([2, 3]));
4077 assert(bd
.dropBackOne().equal([1, 2]));
4081 Create a range which repeats one value.
4084 value = the _value to repeat
4085 n = the number of times to repeat `value`
4088 If `n` is not defined, an infinite random access range
4091 If `n` is defined, a random access range with slicing.
4096 import std
.typecons
: Rebindable2
;
4098 // Store a rebindable T to make Repeat assignable.
4099 Rebindable2
!T _value
;
4102 /// Range primitives
4103 @property inout(T
) front() inout { return _value
.get
; }
4106 @property inout(T
) back() inout { return _value
.get
; }
4109 enum bool empty
= false;
4118 @property auto save() inout { return this; }
4121 inout(T
) opIndex(size_t
) inout { return _value
.get
; }
4124 auto opSlice(size_t i
, size_t j
)
4129 "Attempting to slice a Repeat with a larger first argument than the second."
4134 return this.takeExactly(j
- i
);
4136 private static struct DollarToken
{}
4139 enum opDollar
= DollarToken
.init
;
4142 auto opSlice(size_t
, DollarToken
) inout { return this; }
4146 Repeat
!T
repeat(T
)(T value
)
4148 import std
.typecons
: Rebindable2
;
4150 return Repeat
!T(Rebindable2
!T(value
));
4154 pure @safe nothrow unittest
4156 import std
.algorithm
.comparison
: equal
;
4158 assert(5.repeat().take(4).equal([5, 5, 5, 5]));
4161 pure @safe nothrow unittest
4163 import std
.algorithm
.comparison
: equal
;
4166 alias R
= typeof(r
);
4167 static assert(isBidirectionalRange
!R
);
4168 static assert(isForwardRange
!R
);
4169 static assert(isInfinite
!R
);
4170 static assert(hasSlicing
!R
);
4172 assert(r
.back
== 5);
4173 assert(r
.front
== 5);
4174 assert(r
.take(4).equal([ 5, 5, 5, 5 ]));
4175 assert(r
[0 .. 4].equal([ 5, 5, 5, 5 ]));
4178 assert(r2
.back
== 5);
4179 assert(r2
.front
== 5);
4183 Take
!(Repeat
!T
) repeat(T
)(T value
, size_t n
)
4185 return take(repeat(value
), n
);
4189 pure @safe nothrow unittest
4191 import std
.algorithm
.comparison
: equal
;
4193 assert(5.repeat(4).equal([5, 5, 5, 5]));
4196 // https://issues.dlang.org/show_bug.cgi?id=12007
4197 pure @safe nothrow unittest
4200 Repeat
!(immutable int) ri
;
4202 Repeat
!(immutable C
) rc
;
4205 import std
.algorithm
.setops
: cartesianProduct
;
4206 import std
.algorithm
.comparison
: equal
;
4207 import std
.typecons
: tuple
;
4208 immutable int[] A
= [1,2,3];
4209 immutable int[] B
= [4,5,6];
4211 assert(equal(cartesianProduct(A
,B
),
4213 tuple(1, 4), tuple(1, 5), tuple(1, 6),
4214 tuple(2, 4), tuple(2, 5), tuple(2, 6),
4215 tuple(3, 4), tuple(3, 5), tuple(3, 6),
4220 Given callable ($(REF isCallable, std,traits)) `fun`, create as a range
4221 whose front is defined by successive calls to `fun()`.
4222 This is especially useful to call function with global side effects (random
4223 functions), or to create ranges expressed as a single delegate, rather than
4224 an entire `front`/`popFront`/`empty` structure.
4225 `fun` maybe be passed either a template alias parameter (existing
4226 function, delegate, struct type defining `static opCall`) or
4227 a run-time value argument (delegate, function object).
4228 The result range models an InputRange
4229 ($(REF isInputRange, std,range,primitives)).
4230 The resulting range will call `fun()` on construction, and every call to
4231 `popFront`, and the cached value will be returned when `front` is called.
4233 Returns: an `inputRange` where each element represents another call to fun.
4235 auto generate(Fun
)(Fun fun
)
4238 auto gen
= Generator
!(Fun
)(fun
);
4239 gen
.popFront(); // prime the first element
4244 auto generate(alias fun
)()
4247 auto gen
= Generator
!(fun
)();
4248 gen
.popFront(); // prime the first element
4253 @safe pure nothrow unittest
4255 import std
.algorithm
.comparison
: equal
;
4256 import std
.algorithm
.iteration
: map
;
4259 auto powersOfTwo
= generate
!(() => i
*= 2)().take(10);
4260 assert(equal(powersOfTwo
, iota(1, 11).map
!"2^^a"()));
4264 @safe pure nothrow unittest
4266 import std
.algorithm
.comparison
: equal
;
4268 //Returns a run-time delegate
4269 auto infiniteIota(T
)(T low
, T high
)
4272 return (){if (i
== high
) i
= low
; return i
++;};
4274 //adapted as a range.
4275 assert(equal(generate(infiniteIota(1, 4)).take(10), [1, 2, 3, 1, 2, 3, 1, 2, 3, 1]));
4281 import std
.format
: format
;
4282 import std
.random
: uniform
;
4284 auto r
= generate
!(() => uniform(0, 6)).take(10);
4285 format("%(%s %)", r
);
4288 private struct Generator(Fun
...)
4290 static assert(Fun
.length
== 1);
4291 static assert(isInputRange
!Generator
);
4292 import std
.traits
: FunctionAttribute
, functionAttributes
, ReturnType
;
4295 static if (is(Fun
[0]))
4300 enum returnByRef_
= (functionAttributes
!fun
& FunctionAttribute
.ref_
) ?
true : false;
4302 import std
.traits
: hasIndirections
;
4303 static if (!hasIndirections
!(ReturnType
!fun
))
4304 alias RetType
= Unqual
!(ReturnType
!fun
);
4306 alias RetType
= ReturnType
!fun
;
4308 static if (returnByRef_
)
4313 /// Range primitives
4316 static if (returnByRef_
)
4319 ref front() @property
4332 auto front() @property
4344 @safe nothrow unittest
4346 import std
.algorithm
.comparison
: equal
;
4350 static ubyte opCall() { return 5 ; }
4353 assert(equal(generate
!StaticOpCall().take(10), repeat(5).take(10)));
4358 import std
.algorithm
.comparison
: equal
;
4362 ubyte opCall() @safe pure { return 5 ; }
4366 assert(equal(generate(op
).take(10), repeat(5).take(10)));
4369 // verify ref mechanism works
4370 @system nothrow unittest
4381 foreach (ref x
; generate
!(fun
).take(20))
4385 import std
.algorithm
.comparison
: equal
;
4386 assert(equal(arr
[], iota(12, 32, 2)));
4389 // assure front isn't the mechanism to make generate go to the next element.
4393 auto g
= generate
!(() => ++i
);
4395 assert(f
== g
.front
);
4396 g
= g
.drop(5); // reassign because generate caches
4397 assert(g
.front
== f
+ 5);
4400 // https://issues.dlang.org/show_bug.cgi?id=23319
4401 @safe pure nothrow unittest
4403 auto b
= generate
!(() => const(int)(42));
4404 assert(b
.front
== 42);
4408 Repeats the given forward range ad infinitum. If the original range is
4409 infinite (fact that would make `Cycle` the identity application),
4410 `Cycle` detects that and aliases itself to the range type
4411 itself. That works for non-forward ranges too.
4412 If the original range has random access, `Cycle` offers
4413 random access and also offers a constructor taking an initial position
4414 `index`. `Cycle` works with static arrays in addition to ranges,
4415 mostly for performance reasons.
4417 Note: The input range must not be empty.
4419 Tip: This is a great way to implement simple circular buffers.
4422 if (isForwardRange
!R
&& !isInfinite
!R
)
4424 static if (isRandomAccessRange
!R
&& hasLength
!R
)
4426 private R _original
;
4427 private size_t _index
;
4429 /// Range primitives
4430 this(R input
, size_t index
= 0)
4433 _index
= index
% _original
.length
;
4437 @property auto ref front()
4439 return _original
[_index
];
4442 static if (is(typeof((cast(const R
)_original
)[_index
])))
4445 @property auto ref front() const
4447 return _original
[_index
];
4451 static if (hasAssignableElements
!R
)
4454 @property void front(ElementType
!R val
)
4456 import core
.lifetime
: forward
;
4458 // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542
4459 _original
[_index
] = __ctfe ? val
: forward
!val
;
4464 enum bool empty
= false;
4470 if (_index
>= _original
.length
)
4475 auto ref opIndex(size_t n
)
4477 return _original
[(n
+ _index
) % _original
.length
];
4480 static if (is(typeof((cast(const R
)_original
)[_index
])) &&
4481 is(typeof((cast(const R
)_original
).length
)))
4484 auto ref opIndex(size_t n
) const
4486 return _original
[(n
+ _index
) % _original
.length
];
4490 static if (hasAssignableElements
!R
)
4493 void opIndexAssign(ElementType
!R val
, size_t n
)
4495 _original
[(n
+ _index
) % _original
.length
] = val
;
4500 @property Cycle
save()
4502 //No need to call _original.save, because Cycle never actually modifies _original
4503 return Cycle(_original
, _index
);
4506 private static struct DollarToken
{}
4509 enum opDollar
= DollarToken
.init
;
4511 static if (hasSlicing
!R
)
4514 auto opSlice(size_t i
, size_t j
)
4521 return this[i
.. $].takeExactly(j
- i
);
4525 auto opSlice(size_t i
, DollarToken
)
4527 return typeof(this)(_original
, _index
+ i
);
4533 private R _original
;
4540 _current
= input
.save
;
4543 private this(R original
, R current
)
4545 _original
= original
;
4550 @property auto ref front()
4552 return _current
.front
;
4555 static if (is(typeof((cast(const R
)_current
).front
)))
4558 @property auto ref front() const
4560 return _current
.front
;
4564 static if (hasAssignableElements
!R
)
4567 @property auto front(ElementType
!R val
)
4569 import core
.lifetime
: forward
;
4571 // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542
4572 return _current
.front
= __ctfe ? val
: forward
!val
;
4577 enum bool empty
= false;
4582 _current
.popFront();
4584 _current
= _original
.save
;
4588 @property Cycle
save()
4590 //No need to call _original.save, because Cycle never actually modifies _original
4591 return Cycle(_original
, _current
.save
);
4605 if (isStaticArray
!R
)
4607 private alias ElementType
= typeof(R
.init
[0]);
4608 private ElementType
* _ptr
;
4609 private size_t _index
;
4613 /// Range primitives
4614 this(ref R input
, size_t index
= 0) @system
4617 _index
= index
% R
.length
;
4621 @property ref inout(ElementType
) front() inout @safe
4623 static ref auto trustedPtrIdx(typeof(_ptr
) p
, size_t idx
) @trusted
4627 return trustedPtrIdx(_ptr
, _index
);
4631 enum bool empty
= false;
4634 void popFront() @safe
4637 if (_index
>= R
.length
)
4642 ref inout(ElementType
) opIndex(size_t n
) inout @safe
4644 static ref auto trustedPtrIdx(typeof(_ptr
) p
, size_t idx
) @trusted
4646 return p
[idx
% R
.length
];
4648 return trustedPtrIdx(_ptr
, n
+ _index
);
4652 @property inout(Cycle
) save() inout @safe
4657 private static struct DollarToken
{}
4659 enum opDollar
= DollarToken
.init
;
4662 auto opSlice(size_t i
, size_t j
) @safe
4667 "Attempting to slice a Repeat with a larger first argument than the second."
4672 return this[i
.. $].takeExactly(j
- i
);
4676 inout(typeof(this)) opSlice(size_t i
, DollarToken
) inout @safe
4678 static auto trustedCtor(typeof(_ptr
) p
, size_t idx
) @trusted
4680 return cast(inout) Cycle(*cast(R
*)(p
), idx
);
4682 return trustedCtor(_ptr
, _index
+ i
);
4687 auto cycle(R
)(R input
)
4690 static assert(isForwardRange
!R || isInfinite
!R
,
4691 "Cycle requires a forward range argument unless it's statically known"
4692 ~ " to be infinite");
4693 assert(!input
.empty
, "Attempting to pass an empty input to cycle");
4694 static if (isInfinite
!R
) return input
;
4695 else return Cycle
!R(input
);
4701 import std
.algorithm
.comparison
: equal
;
4702 import std
.range
: cycle
, take
;
4704 // Here we create an infinitive cyclic sequence from [1, 2]
4705 // (i.e. get here [1, 2, 1, 2, 1, 2 and so on]) then
4706 // take 5 elements of this sequence (so we have [1, 2, 1, 2, 1])
4707 // and compare them with the expected values for equality.
4708 assert(cycle([1, 2]).take(5).equal([ 1, 2, 1, 2, 1 ]));
4712 Cycle
!R
cycle(R
)(R input
, size_t index
= 0)
4713 if (isRandomAccessRange
!R
&& !isInfinite
!R
)
4715 assert(!input
.empty
, "Attempting to pass an empty input to cycle");
4716 return Cycle
!R(input
, index
);
4720 Cycle
!R
cycle(R
)(ref R input
, size_t index
= 0) @system
4721 if (isStaticArray
!R
)
4723 return Cycle
!R(input
, index
);
4726 @safe nothrow unittest
4728 import std
.algorithm
.comparison
: equal
;
4729 import std
.internal
.test.dummyrange
: AllDummyRanges
;
4731 static assert(isForwardRange
!(Cycle
!(uint[])));
4733 // Make sure ref is getting propagated properly.
4734 int[] nums
= [1,2,3];
4735 auto c2
= cycle(nums
);
4737 assert(nums
[0] == 2);
4739 immutable int[] immarr
= [1, 2, 3];
4741 foreach (DummyType
; AllDummyRanges
)
4743 static if (isForwardRange
!DummyType
)
4746 auto cy
= cycle(dummy
);
4747 static assert(isForwardRange
!(typeof(cy
)));
4748 auto t
= take(cy
, 20);
4749 assert(equal(t
, [1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10]));
4752 assert(cRange
.front
== 1);
4754 static if (hasAssignableElements
!DummyType
)
4758 scope(exit
) cy
.front
= 1;
4759 assert(dummy
.front
== 66);
4762 static if (isRandomAccessRange
!DummyType
)
4766 scope(exit
) cy
[10] = 1;
4767 assert(dummy
.front
== 66);
4770 assert(cRange
[10] == 1);
4774 static if (hasSlicing
!DummyType
)
4776 auto slice
= cy
[5 .. 15];
4777 assert(equal(slice
, [6, 7, 8, 9, 10, 1, 2, 3, 4, 5]));
4778 static assert(is(typeof(slice
) == typeof(takeExactly(cy
, 5))));
4780 auto infSlice
= cy
[7 .. $];
4781 assert(equal(take(infSlice
, 5), [8, 9, 10, 1, 2]));
4782 static assert(isInfinite
!(typeof(infSlice
)));
4788 @system nothrow unittest // For static arrays.
4790 import std
.algorithm
.comparison
: equal
;
4792 int[3] a
= [ 1, 2, 3 ];
4793 static assert(isStaticArray
!(typeof(a
)));
4795 assert(a
.ptr
== c
._ptr
);
4796 assert(equal(take(cycle(a
), 5), [ 1, 2, 3, 1, 2 ][]));
4797 static assert(isForwardRange
!(typeof(c
)));
4799 // Test qualifiers on slicing.
4800 alias C
= typeof(c
);
4801 static assert(is(typeof(c
[1 .. $]) == C
));
4803 static assert(is(typeof(cConst
[1 .. $]) == const(C
)));
4806 @safe nothrow unittest // For infinite ranges
4811 @property int front() { return 0; }
4813 auto save() { return this; }
4815 struct NonForwardInfRange
4818 @property int front() { return 0; }
4823 NonForwardInfRange j
;
4826 //make sure it can alias out even non-forward infinite ranges
4827 static assert(is(typeof(j
.cycle
) == typeof(j
)));
4832 import std
.algorithm
.comparison
: equal
;
4834 int[5] arr
= [0, 1, 2, 3, 4];
4835 auto cleD
= cycle(arr
[]); //Dynamic
4836 assert(equal(cleD
[5 .. 10], arr
[]));
4838 //n is a multiple of 5 worth about 3/4 of size_t.max
4839 auto n
= size_t
.max
/4 + size_t
.max
/2;
4842 //Test index overflow
4843 foreach (_
; 0 .. 10)
4845 cleD
= cleD
[n
.. $];
4846 assert(equal(cleD
[5 .. 10], arr
[]));
4850 @system @nogc nothrow unittest
4852 import std
.algorithm
.comparison
: equal
;
4854 int[5] arr
= [0, 1, 2, 3, 4];
4855 auto cleS
= cycle(arr
); //Static
4856 assert(equal(cleS
[5 .. 10], arr
[]));
4858 //n is a multiple of 5 worth about 3/4 of size_t.max
4859 auto n
= size_t
.max
/4 + size_t
.max
/2;
4862 //Test index overflow
4863 foreach (_
; 0 .. 10)
4865 cleS
= cleS
[n
.. $];
4866 assert(equal(cleS
[5 .. 10], arr
[]));
4872 import std
.algorithm
.comparison
: equal
;
4875 auto cleS
= cycle(arr
);
4876 cleS
= cleS
[10 .. $];
4877 assert(equal(cleS
[5 .. 10], 0.repeat(5)));
4878 assert(cleS
.front
== 0);
4881 // https://issues.dlang.org/show_bug.cgi?id=10845
4884 import std
.algorithm
.comparison
: equal
;
4885 import std
.algorithm
.iteration
: filter
;
4887 auto a
= inputRangeObject(iota(3).filter
!"true");
4888 assert(equal(cycle(a
).take(10), [0, 1, 2, 0, 1, 2, 0, 1, 2, 0]));
4891 // https://issues.dlang.org/show_bug.cgi?id=12177
4894 static assert(__traits(compiles
, recurrence
!q
{a
[n
- 1] ~ a
[n
- 2]}("1", "0")));
4897 // https://issues.dlang.org/show_bug.cgi?id=13390
4900 import core
.exception
: AssertError
;
4901 import std
.exception
: assertThrown
;
4902 assertThrown
!AssertError(cycle([0, 1, 2][0 .. 0]));
4905 // https://issues.dlang.org/show_bug.cgi?id=18657
4908 import std
.algorithm
.comparison
: equal
;
4910 auto r
= refRange(&s
).cycle
.take(4);
4911 assert(equal(r
.save
, "foof"));
4912 assert(equal(r
.save
, "foof"));
4915 // https://issues.dlang.org/show_bug.cgi?id=24481
4918 import std
.algorithm
.iteration
: filter
;
4924 void opAssign()(auto ref const(typeof(this)) that
) const { called
= true; }
4927 const(Handle
)[3] arr
= [Handle(0), Handle(1), Handle(2)];
4929 auto range
= arr
[].cycle().take(5);
4932 range
.front
= Handle(42);
4936 auto range
= arr
[].filter
!(a
=> true)().cycle().take(5);
4939 range
.front
= Handle(42);
4944 private alias lengthType(R
) = typeof(R
.init
.length
.init
);
4947 Iterate several ranges in lockstep. The element type is a proxy tuple
4948 that allows accessing the current element in the `n`th range by
4951 `zip` is similar to $(LREF lockstep), but `lockstep` doesn't
4952 bundle its elements and uses the `opApply` protocol.
4953 `lockstep` allows reference access to the elements in
4954 `foreach` iterations.
4957 sp = controls what `zip` will do if the ranges are different lengths
4958 ranges = the ranges to zip together
4960 At minimum, an input range. `Zip` offers the lowest range facilities
4961 of all components, e.g. it offers random access iff all ranges offer
4962 random access, and also offers mutation and swapping if all ranges offer
4963 it. Due to this, `Zip` is extremely powerful because it allows manipulating
4964 several ranges in lockstep.
4966 An `Exception` if all of the ranges are not the same length and
4967 `sp` is set to `StoppingPolicy.requireSameLength`.
4969 Limitations: The `@nogc` and `nothrow` attributes cannot be inferred for
4970 the `Zip` struct because $(LREF StoppingPolicy) can vary at runtime. This
4971 limitation is not shared by the anonymous range returned by the `zip`
4972 function when not given an explicit `StoppingPolicy` as an argument.
4974 struct Zip(Ranges
...)
4975 if (Ranges
.length
&& allSatisfy
!(isInputRange
, Ranges
))
4977 import std
.format
: format
; //for generic mixins
4978 import std
.typecons
: Tuple
;
4982 alias ElementType
= Tuple
!(staticMap
!(.ElementType
, R
));
4983 private StoppingPolicy stoppingPolicy
= StoppingPolicy
.shortest
;
4986 Builds an object. Usually this is invoked indirectly by using the
4987 $(LREF zip) function.
4989 this(R rs
, StoppingPolicy s
= StoppingPolicy
.shortest
)
4996 Returns `true` if the range is at end. The test depends on the
4999 static if (allSatisfy
!(isInfinite
, R
))
5001 // BUG: Doesn't propagate infiniteness if only some ranges are infinite
5002 // and s == StoppingPolicy.longest. This isn't fixable in the
5003 // current design since StoppingPolicy is known only at runtime.
5004 enum bool empty
= false;
5009 @property bool empty()
5011 import std
.exception
: enforce
;
5012 import std
.meta
: anySatisfy
;
5014 final switch (stoppingPolicy
)
5016 case StoppingPolicy
.shortest
:
5017 foreach (i
, Unused
; R
)
5019 if (ranges
[i
].empty
) return true;
5022 case StoppingPolicy
.longest
:
5023 static if (anySatisfy
!(isInfinite
, R
))
5029 foreach (i
, Unused
; R
)
5031 if (!ranges
[i
].empty
) return false;
5035 case StoppingPolicy
.requireSameLength
:
5036 foreach (i
, Unused
; R
[1 .. $])
5038 enforce(ranges
[0].empty
==
5039 ranges
[i
+ 1].empty
,
5040 "Inequal-length ranges passed to Zip");
5042 return ranges
[0].empty
;
5048 static if (allSatisfy
!(isForwardRange
, R
))
5051 @property Zip
save()
5053 //Zip(ranges[0].save, ranges[1].save, ..., stoppingPolicy)
5054 return mixin (q
{Zip(%(ranges
[%s
].save
%|
, %), stoppingPolicy
)}.format(iota(0, R
.length
)));
5058 private .ElementType
!(R
[i
]) tryGetInit(size_t i
)()
5060 alias E
= .ElementType
!(R
[i
]);
5061 static if (!is(typeof({static E i
;})))
5062 throw new Exception("Range with non-default constructable elements exhausted.");
5068 Returns the current iterated element.
5070 @property ElementType
front()
5072 @property tryGetFront(size_t i
)(){return ranges
[i
].empty ? tryGetInit
!i() : ranges
[i
].front
;}
5073 //ElementType(tryGetFront!0, tryGetFront!1, ...)
5074 return mixin(q
{ElementType(%(tryGetFront
!%s
, %))}.format(iota(0, R
.length
)));
5078 Sets the front of all iterated ranges.
5080 static if (allSatisfy
!(hasAssignableElements
, R
))
5082 @property void front(ElementType v
)
5084 foreach (i
, Unused
; R
)
5086 if (!ranges
[i
].empty
)
5088 ranges
[i
].front
= v
[i
];
5095 Moves out the front.
5097 static if (allSatisfy
!(hasMobileElements
, R
))
5099 ElementType
moveFront()
5101 @property tryMoveFront(size_t i
)(){return ranges
[i
].empty ? tryGetInit
!i() : ranges
[i
].moveFront();}
5102 //ElementType(tryMoveFront!0, tryMoveFront!1, ...)
5103 return mixin(q
{ElementType(%(tryMoveFront
!%s
, %))}.format(iota(0, R
.length
)));
5108 Returns the rightmost element.
5110 static if (allSatisfy
!(isBidirectionalRange
, R
))
5112 @property ElementType
back()
5114 //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness
5116 @property tryGetBack(size_t i
)(){return ranges
[i
].empty ? tryGetInit
!i() : ranges
[i
].back
;}
5117 //ElementType(tryGetBack!0, tryGetBack!1, ...)
5118 return mixin(q
{ElementType(%(tryGetBack
!%s
, %))}.format(iota(0, R
.length
)));
5124 static if (allSatisfy
!(hasMobileElements
, R
))
5126 ElementType
moveBack()
5128 //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness
5130 @property tryMoveBack(size_t i
)(){return ranges
[i
].empty ? tryGetInit
!i() : ranges
[i
].moveBack();}
5131 //ElementType(tryMoveBack!0, tryMoveBack!1, ...)
5132 return mixin(q
{ElementType(%(tryMoveBack
!%s
, %))}.format(iota(0, R
.length
)));
5137 Returns the current iterated element.
5139 static if (allSatisfy
!(hasAssignableElements
, R
))
5141 @property void back(ElementType v
)
5143 //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness.
5144 //Not sure the call is even legal for StoppingPolicy.longest
5146 foreach (i
, Unused
; R
)
5148 if (!ranges
[i
].empty
)
5150 ranges
[i
].back
= v
[i
];
5158 Advances to the next element in all controlled ranges.
5162 import std
.exception
: enforce
;
5164 final switch (stoppingPolicy
)
5166 case StoppingPolicy
.shortest
:
5167 foreach (i
, Unused
; R
)
5169 assert(!ranges
[i
].empty
);
5170 ranges
[i
].popFront();
5173 case StoppingPolicy
.longest
:
5174 foreach (i
, Unused
; R
)
5176 if (!ranges
[i
].empty
) ranges
[i
].popFront();
5179 case StoppingPolicy
.requireSameLength
:
5180 foreach (i
, Unused
; R
)
5182 enforce(!ranges
[i
].empty
, "Invalid Zip object");
5183 ranges
[i
].popFront();
5190 Calls `popBack` for all controlled ranges.
5192 static if (allSatisfy
!(isBidirectionalRange
, R
))
5196 //TODO: Fixme! In case of jaggedness, this is wrong.
5197 import std
.exception
: enforce
;
5199 final switch (stoppingPolicy
)
5201 case StoppingPolicy
.shortest
:
5202 foreach (i
, Unused
; R
)
5204 assert(!ranges
[i
].empty
);
5205 ranges
[i
].popBack();
5208 case StoppingPolicy
.longest
:
5209 foreach (i
, Unused
; R
)
5211 if (!ranges
[i
].empty
) ranges
[i
].popBack();
5214 case StoppingPolicy
.requireSameLength
:
5215 foreach (i
, Unused
; R
)
5217 enforce(!ranges
[i
].empty
, "Invalid Zip object");
5218 ranges
[i
].popBack();
5226 Returns the length of this range. Defined only if all ranges define
5229 static if (allSatisfy
!(hasLength
, R
))
5231 @property auto length()
5233 static if (Ranges
.length
== 1)
5234 return ranges
[0].length
;
5237 if (stoppingPolicy
== StoppingPolicy
.requireSameLength
)
5238 return ranges
[0].length
;
5240 //[min|max](ranges[0].length, ranges[1].length, ...)
5241 import std
.algorithm
.comparison
: min
, max
;
5242 if (stoppingPolicy
== StoppingPolicy
.shortest
)
5243 return mixin(q
{min(%(ranges
[%s
].length
%|
, %))}.format(iota(0, R
.length
)));
5245 return mixin(q
{max(%(ranges
[%s
].length
%|
, %))}.format(iota(0, R
.length
)));
5249 alias opDollar
= length
;
5253 Returns a slice of the range. Defined only if all range define
5256 static if (allSatisfy
!(hasSlicing
, R
))
5258 auto opSlice(size_t from
, size_t to
)
5260 //Slicing an infinite range yields the type Take!R
5261 //For finite ranges, the type Take!R aliases to R
5262 alias ZipResult
= Zip
!(staticMap
!(Take
, R
));
5264 //ZipResult(ranges[0][from .. to], ranges[1][from .. to], ..., stoppingPolicy)
5265 return mixin (q
{ZipResult(%(ranges
[%s
][from
.. to
]%|
, %), stoppingPolicy
)}.format(iota(0, R
.length
)));
5270 Returns the `n`th element in the composite range. Defined if all
5271 ranges offer random access.
5273 static if (allSatisfy
!(isRandomAccessRange
, R
))
5275 ElementType
opIndex(size_t n
)
5277 //TODO: Fixme! This may create an out of bounds access
5278 //for StoppingPolicy.longest
5280 //ElementType(ranges[0][n], ranges[1][n], ...)
5281 return mixin (q
{ElementType(%(ranges
[%s
][n
]%|
, %))}.format(iota(0, R
.length
)));
5285 Assigns to the `n`th element in the composite range. Defined if
5286 all ranges offer random access.
5288 static if (allSatisfy
!(hasAssignableElements
, R
))
5290 void opIndexAssign(ElementType v
, size_t n
)
5292 //TODO: Fixme! Not sure the call is even legal for StoppingPolicy.longest
5293 foreach (i
, Range
; R
)
5295 ranges
[i
][n
] = v
[i
];
5301 Destructively reads the `n`th element in the composite
5302 range. Defined if all ranges offer random access.
5304 static if (allSatisfy
!(hasMobileElements
, R
))
5306 ElementType
moveAt(size_t n
)
5308 //TODO: Fixme! This may create an out of bounds access
5309 //for StoppingPolicy.longest
5311 //ElementType(ranges[0].moveAt(n), ranges[1].moveAt(n), ..., )
5312 return mixin (q
{ElementType(%(ranges
[%s
].moveAt(n
)%|
, %))}.format(iota(0, R
.length
)));
5319 auto zip(Ranges
...)(Ranges ranges
)
5320 if (Ranges
.length
&& allSatisfy
!(isInputRange
, Ranges
))
5322 import std
.meta
: anySatisfy
, templateOr
;
5323 static if (allSatisfy
!(isInfinite
, Ranges
) || Ranges
.length
== 1)
5325 return ZipShortest
!(Ranges
)(ranges
);
5327 else static if (allSatisfy
!(isBidirectionalRange
, Ranges
))
5329 static if (allSatisfy
!(templateOr
!(isInfinite
, hasLength
), Ranges
)
5330 && allSatisfy
!(templateOr
!(isInfinite
, hasSlicing
), Ranges
)
5331 && allSatisfy
!(isBidirectionalRange
, staticMap
!(Take
, Ranges
)))
5333 // If all the ranges are bidirectional, if possible slice them to
5334 // the same length to simplify the implementation.
5335 static assert(anySatisfy
!(hasLength
, Ranges
));
5336 static foreach (i
, Range
; Ranges
)
5337 static if (hasLength
!Range
)
5339 static if (!is(typeof(minLen
) == size_t
))
5340 size_t minLen
= ranges
[i
].length
;
5343 const x
= ranges
[i
].length
;
5344 if (x
< minLen
) minLen
= x
;
5347 import std
.format
: format
;
5348 static if (!anySatisfy
!(isInfinite
, Ranges
))
5349 return mixin(`ZipShortest!(Yes.allKnownSameLength, staticMap!(Take, Ranges))`~
5350 `(%(ranges[%s][0 .. minLen]%|, %))`.format(iota(0, Ranges
.length
)));
5352 return mixin(`ZipShortest!(Yes.allKnownSameLength, staticMap!(Take, Ranges))`~
5353 `(%(take(ranges[%s], minLen)%|, %))`.format(iota(0, Ranges
.length
)));
5355 else static if (allSatisfy
!(isRandomAccessRange
, Ranges
))
5357 // We can't slice but we can still use random access to ensure
5358 // "back" is retrieving the same index for each range.
5359 return ZipShortest
!(Ranges
)(ranges
);
5363 // If bidirectional range operations would not be supported by
5364 // ZipShortest that might have actually been a bug since Zip
5365 // supported `back` without verifying that each range had the
5366 // same length, but for the sake of backwards compatibility
5367 // use the old Zip to continue supporting them.
5368 return Zip
!Ranges(ranges
);
5373 return ZipShortest
!(Ranges
)(ranges
);
5378 @nogc nothrow pure @safe unittest
5380 import std
.algorithm
.comparison
: equal
;
5381 import std
.algorithm
.iteration
: map
;
5384 auto arr
= only(0, 1, 2);
5385 auto part1
= zip(arr
, arr
.dropOne
).map
!"a[0] + a[1]";
5386 assert(part1
.equal(only(1, 3)));
5390 nothrow pure @safe unittest
5392 import std
.conv
: to
;
5394 int[] a
= [ 1, 2, 3 ];
5395 string
[] b
= [ "a", "b", "c" ];
5398 foreach (tup
; zip(a
, b
))
5400 result
~= tup
[0].to
!string
~ tup
[1];
5403 assert(result
== [ "1a", "2b", "3c" ]);
5406 // unpacking tuple elements with foreach
5407 foreach (e1
, e2
; zip(a
, b
))
5409 assert(e1
== a
[idx
]);
5410 assert(e2
== b
[idx
]);
5415 /// `zip` is powerful - the following code sorts two arrays in parallel:
5416 nothrow pure @safe unittest
5418 import std
.algorithm
.sorting
: sort
;
5420 int[] a
= [ 1, 2, 3 ];
5421 string
[] b
= [ "a", "c", "b" ];
5422 zip(a
, b
).sort
!((t1
, t2
) => t1
[0] > t2
[0]);
5424 assert(a
== [ 3, 2, 1 ]);
5425 // b is sorted according to a's sorting
5426 assert(b
== [ "b", "c", "a" ]);
5430 auto zip(Ranges
...)(StoppingPolicy sp
, Ranges ranges
)
5431 if (Ranges
.length
&& allSatisfy
!(isInputRange
, Ranges
))
5433 return Zip
!Ranges(ranges
, sp
);
5437 Dictates how iteration in a $(LREF zip) and $(LREF lockstep) should stop.
5438 By default stop at the end of the shortest of all ranges.
5442 /// Stop when the shortest range is exhausted
5444 /// Stop when the longest range is exhausted
5446 /// Require that all ranges are equal
5453 import std
.algorithm
.comparison
: equal
;
5454 import std
.exception
: assertThrown
;
5455 import std
.range
.primitives
;
5456 import std
.typecons
: tuple
;
5459 auto b
= [4, 5, 6, 7];
5461 auto shortest
= zip(StoppingPolicy
.shortest
, a
, b
);
5462 assert(shortest
.equal([
5468 auto longest
= zip(StoppingPolicy
.longest
, a
, b
);
5469 assert(longest
.equal([
5476 auto same
= zip(StoppingPolicy
.requireSameLength
, a
, b
);
5478 assertThrown
!Exception(same
.popFront
);
5482 Non-public. Like $(LREF Zip) with `StoppingPolicy.shortest`
5483 except it properly implements `back` and `popBack` in the
5484 case of uneven ranges or disables those operations when
5485 it is not possible to guarantee they are correct.
5487 package template ZipShortest(Ranges
...)
5488 if (Ranges
.length
&& __traits(compiles
,
5490 static assert(allSatisfy
!(isInputRange
, Ranges
));
5493 alias ZipShortest
= .ZipShortest
!(
5494 Ranges
.length
== 1 || allSatisfy
!(isInfinite
, Ranges
)
5495 ? Yes
.allKnownSameLength
5496 : No
.allKnownSameLength
,
5499 /+ non-public, ditto +/
5500 package struct ZipShortest(Flag
!"allKnownSameLength" allKnownSameLength
, Ranges
...)
5501 if (Ranges
.length
&& allSatisfy
!(isInputRange
, Ranges
))
5503 import std
.format
: format
; //for generic mixins
5504 import std
.meta
: anySatisfy
, templateOr
;
5505 import std
.typecons
: Tuple
;
5507 deprecated("Use of an undocumented alias R.")
5508 alias R
= Ranges
; // Unused here but defined in case library users rely on it.
5509 private Ranges ranges
;
5510 alias ElementType
= Tuple
!(staticMap
!(.ElementType
, Ranges
));
5513 Builds an object. Usually this is invoked indirectly by using the
5514 $(LREF zip) function.
5522 Returns `true` if the range is at end.
5524 static if (allKnownSameLength ? anySatisfy
!(isInfinite
, Ranges
)
5525 : allSatisfy
!(isInfinite
, Ranges
))
5527 enum bool empty
= false;
5531 @property bool empty()
5533 static if (allKnownSameLength
)
5535 return ranges
[0].empty
;
5539 static foreach (i
; 0 .. Ranges
.length
)
5541 if (ranges
[i
].empty
)
5550 Forward range primitive. Only present if each constituent range is a
5553 static if (allSatisfy
!(isForwardRange
, Ranges
))
5554 @property typeof(this) save()
5556 return mixin(`typeof(return)(%(ranges[%s].save%|, %))`.format(iota(0, Ranges
.length
)));
5560 Returns the current iterated element.
5562 @property ElementType
front()
5564 return mixin(`typeof(return)(%(ranges[%s].front%|, %))`.format(iota(0, Ranges
.length
)));
5568 Sets the front of all iterated ranges. Only present if each constituent
5569 range has assignable elements.
5571 static if (allSatisfy
!(hasAssignableElements
, Ranges
))
5572 @property void front()(ElementType v
)
5574 static foreach (i
; 0 .. Ranges
.length
)
5575 ranges
[i
].front
= v
[i
];
5579 Moves out the front. Present if each constituent range has mobile elements.
5581 static if (allSatisfy
!(hasMobileElements
, Ranges
))
5582 ElementType
moveFront()()
5584 return mixin(`typeof(return)(%(ranges[%s].moveFront()%|, %))`.format(iota(0, Ranges
.length
)));
5587 private enum bool isBackWellDefined
= allSatisfy
!(isBidirectionalRange
, Ranges
)
5588 && (allKnownSameLength
5589 || allSatisfy
!(isRandomAccessRange
, Ranges
)
5590 // Could also add the case where there is one non-infinite bidirectional
5591 // range that defines `length` and all others are infinite random access
5592 // ranges. Adding this would require appropriate branches in
5593 // back/moveBack/popBack.
5597 Returns the rightmost element. Present if all constituent ranges are
5598 bidirectional and either there is a compile-time guarantee that all
5599 ranges have the same length (in `allKnownSameLength`) or all ranges
5600 provide random access to elements.
5602 static if (isBackWellDefined
)
5603 @property ElementType
back()
5605 static if (allKnownSameLength
)
5607 return mixin(`typeof(return)(%(ranges[%s].back()%|, %))`.format(iota(0, Ranges
.length
)));
5611 const backIndex
= length
- 1;
5612 return mixin(`typeof(return)(%(ranges[%s][backIndex]%|, %))`.format(iota(0, Ranges
.length
)));
5617 Moves out the back. Present if `back` is defined and
5618 each constituent range has mobile elements.
5620 static if (isBackWellDefined
&& allSatisfy
!(hasMobileElements
, Ranges
))
5621 ElementType
moveBack()()
5623 static if (allKnownSameLength
)
5625 return mixin(`typeof(return)(%(ranges[%s].moveBack()%|, %))`.format(iota(0, Ranges
.length
)));
5629 const backIndex
= length
- 1;
5630 return mixin(`typeof(return)(%(ranges[%s].moveAt(backIndex)%|, %))`.format(iota(0, Ranges
.length
)));
5635 Sets the rightmost element. Only present if `back` is defined and
5636 each constituent range has assignable elements.
5638 static if (isBackWellDefined
&& allSatisfy
!(hasAssignableElements
, Ranges
))
5639 @property void back()(ElementType v
)
5641 static if (allKnownSameLength
)
5643 static foreach (i
; 0 .. Ranges
.length
)
5644 ranges
[i
].back
= v
[i
];
5648 const backIndex
= length
- 1;
5649 static foreach (i
; 0 .. Ranges
.length
)
5650 ranges
[i
][backIndex
] = v
[i
];
5655 Calls `popFront` on each constituent range.
5659 static foreach (i
; 0 .. Ranges
.length
)
5660 ranges
[i
].popFront();
5664 Pops the rightmost element. Present if `back` is defined.
5666 static if (isBackWellDefined
)
5669 static if (allKnownSameLength
)
5671 static foreach (i
; 0 .. Ranges
.length
)
5677 static foreach (i
; 0 .. Ranges
.length
)
5678 static if (!isInfinite
!(Ranges
[i
]))
5679 if (ranges
[i
].length
== len
)
5680 ranges
[i
].popBack();
5685 Returns the length of this range. Defined if at least one
5686 constituent range defines `length` and the other ranges all also
5687 define `length` or are infinite, or if at least one constituent
5688 range defines `length` and there is a compile-time guarantee that
5689 all ranges have the same length (in `allKnownSameLength`).
5691 static if (allKnownSameLength
5692 ? anySatisfy
!(hasLength
, Ranges
)
5693 : (anySatisfy
!(hasLength
, Ranges
)
5694 && allSatisfy
!(templateOr
!(isInfinite
, hasLength
), Ranges
)))
5696 @property size_t
length()
5698 static foreach (i
, Range
; Ranges
)
5700 static if (hasLength
!Range
)
5702 static if (!is(typeof(minLen
) == size_t
))
5703 size_t minLen
= ranges
[i
].length
;
5704 else static if (!allKnownSameLength
)
5706 const x
= ranges
[i
].length
;
5707 if (x
< minLen
) minLen
= x
;
5714 alias opDollar
= length
;
5718 Returns a slice of the range. Defined if all constituent ranges
5721 static if (allSatisfy
!(hasSlicing
, Ranges
))
5723 // Note: we will know that all elements of the resultant range
5724 // will have the same length but we cannot change `allKnownSameLength`
5725 // because the `hasSlicing` predicate tests that the result returned
5726 // by `opSlice` has the same type as the receiver.
5727 auto opSlice()(size_t from
, size_t to
)
5729 //(ranges[0][from .. to], ranges[1][from .. to], ...)
5730 enum sliceArgs
= `(%(ranges[%s][from .. to]%|, %))`.format(iota(0, Ranges
.length
));
5731 static if (__traits(compiles
, mixin(`typeof(this)`~sliceArgs
)))
5732 return mixin(`typeof(this)`~sliceArgs
);
5734 // The type is different anyway so we might as well
5735 // explicitly set allKnownSameLength.
5736 return mixin(`ZipShortest!(Yes.allKnownSameLength, staticMap!(Take, Ranges))`
5742 Returns the `n`th element in the composite range. Defined if all
5743 constituent ranges offer random access.
5745 static if (allSatisfy
!(isRandomAccessRange
, Ranges
))
5746 ElementType
opIndex()(size_t n
)
5748 return mixin(`typeof(return)(%(ranges[%s][n]%|, %))`.format(iota(0, Ranges
.length
)));
5752 Sets the `n`th element in the composite range. Defined if all
5753 constituent ranges offer random access and have assignable elements.
5755 static if (allSatisfy
!(isRandomAccessRange
, Ranges
)
5756 && allSatisfy
!(hasAssignableElements
, Ranges
))
5757 void opIndexAssign()(ElementType v
, size_t n
)
5759 static foreach (i
; 0 .. Ranges
.length
)
5760 ranges
[i
][n
] = v
[i
];
5764 Destructively reads the `n`th element in the composite
5765 range. Defined if all constituent ranges offer random
5766 access and have mobile elements.
5768 static if (allSatisfy
!(isRandomAccessRange
, Ranges
)
5769 && allSatisfy
!(hasMobileElements
, Ranges
))
5770 ElementType
moveAt()(size_t n
)
5772 return mixin(`typeof(return)(%(ranges[%s].moveAt(n)%|, %))`.format(iota(0, Ranges
.length
)));
5776 pure @system unittest
5778 import std
.algorithm
.comparison
: equal
;
5779 import std
.algorithm
.iteration
: filter
, map
;
5780 import std
.algorithm
.mutation
: swap
;
5781 import std
.algorithm
.sorting
: sort
;
5783 import std
.exception
: assertThrown
, assertNotThrown
;
5784 import std
.typecons
: tuple
;
5786 int[] a
= [ 1, 2, 3 ];
5787 float[] b
= [ 1.0, 2.0, 3.0 ];
5788 foreach (e
; zip(a
, b
))
5790 assert(e
[0] == e
[1]);
5797 //swap(z.front(), z.back());
5798 sort
!("a[0] < b[0]")(zip(a
, b
));
5799 assert(a
== [1, 2, 3]);
5800 assert(b
== [2.0, 1.0, 3.0]);
5802 auto z
= zip(StoppingPolicy
.requireSameLength
, a
, b
);
5803 assertNotThrown(z
.popBack());
5804 assertNotThrown(z
.popBack());
5805 assertNotThrown(z
.popBack());
5807 assertThrown(z
.popBack());
5810 b
= [ 1.0, 2.0, 3.0 ];
5811 sort
!("a[0] > b[0]")(zip(StoppingPolicy
.requireSameLength
, a
, b
));
5812 assert(a
== [3, 2, 1]);
5813 assert(b
== [3.0, 2.0, 1.0]);
5817 assert(zip(StoppingPolicy
.requireSameLength
, a
, b
).empty
);
5819 // Test infiniteness propagation.
5820 static assert(isInfinite
!(typeof(zip(repeat(1), repeat(1)))));
5822 // Test stopping policies with both value and reference.
5824 auto a2
= [1, 2, 3];
5825 auto stuff
= tuple(tuple(a1
, a2
),
5826 tuple(filter
!"a"(a1
), filter
!"a"(a2
)));
5828 alias FOO
= Zip
!(immutable(int)[], immutable(float)[]);
5830 foreach (t
; stuff
.expand
)
5834 auto zShortest
= zip(arr1
, arr2
);
5835 assert(equal(map
!"a[0]"(zShortest
), [1, 2]));
5836 assert(equal(map
!"a[1]"(zShortest
), [1, 2]));
5839 auto zSame
= zip(StoppingPolicy
.requireSameLength
, arr1
, arr2
);
5840 foreach (elem
; zSame
) {}
5842 } catch (Throwable
) { /* It's supposed to throw.*/ }
5844 auto zLongest
= zip(StoppingPolicy
.longest
, arr1
, arr2
);
5845 assert(!zLongest
.ranges
[0].empty
);
5846 assert(!zLongest
.ranges
[1].empty
);
5848 zLongest
.popFront();
5849 zLongest
.popFront();
5850 assert(!zLongest
.empty
);
5851 assert(zLongest
.ranges
[0].empty
);
5852 assert(!zLongest
.ranges
[1].empty
);
5854 zLongest
.popFront();
5855 assert(zLongest
.empty
);
5858 // https://issues.dlang.org/show_bug.cgi?id=8900
5859 assert(zip([1, 2], repeat('a')).array
== [tuple(1, 'a'), tuple(2, 'a')]);
5860 assert(zip(repeat('a'), [1, 2]).array
== [tuple('a', 1), tuple('a', 2)]);
5862 // https://issues.dlang.org/show_bug.cgi?id=18524
5863 // moveBack instead performs moveFront
5865 auto r
= zip([1,2,3]);
5866 assert(r
.moveBack()[0] == 3);
5867 assert(r
.moveFront()[0] == 1);
5870 // Doesn't work yet. Issues w/ emplace.
5871 // static assert(is(Zip!(immutable int[], immutable float[])));
5874 // These unittests pass, but make the compiler consume an absurd amount
5875 // of RAM and time. Therefore, they should only be run if explicitly
5876 // uncommented when making changes to Zip. Also, running them using
5877 // make -fwin32.mak unittest makes the compiler completely run out of RAM.
5878 // You need to test just this module.
5880 foreach (DummyType1; AllDummyRanges)
5883 foreach (DummyType2; AllDummyRanges)
5886 auto r = zip(d1, d2);
5887 assert(equal(map!"a[0]"(r), [1,2,3,4,5,6,7,8,9,10]));
5888 assert(equal(map!"a[1]"(r), [1,2,3,4,5,6,7,8,9,10]));
5890 static if (isForwardRange!DummyType1 && isForwardRange!DummyType2)
5892 static assert(isForwardRange!(typeof(r)));
5895 static if (isBidirectionalRange!DummyType1 &&
5896 isBidirectionalRange!DummyType2) {
5897 static assert(isBidirectionalRange!(typeof(r)));
5899 static if (isRandomAccessRange!DummyType1 &&
5900 isRandomAccessRange!DummyType2) {
5901 static assert(isRandomAccessRange!(typeof(r)));
5908 nothrow pure @safe unittest
5910 import std
.algorithm
.sorting
: sort
;
5912 auto a
= [5,4,3,2,1];
5913 auto b
= [3,1,2,5,6];
5916 sort
!"a[0] < b[0]"(z
);
5918 assert(a
== [1, 2, 3, 4, 5]);
5919 assert(b
== [6, 5, 2, 1, 3]);
5922 nothrow pure @safe unittest
5924 import std
.algorithm
.comparison
: equal
;
5925 import std
.typecons
: tuple
;
5927 auto LL
= iota(1L, 1000L);
5928 auto z
= zip(LL
, [4]);
5930 assert(equal(z
, [tuple(1L,4)]));
5932 auto LL2
= iota(0L, 500L);
5933 auto z2
= zip([7], LL2
);
5934 assert(equal(z2
, [tuple(7, 0L)]));
5937 // Test for https://issues.dlang.org/show_bug.cgi?id=11196
5940 import std
.exception
: assertThrown
;
5942 static struct S
{ @disable this(); }
5943 assert(zip((S
[5]).init
[]).length
== 5);
5944 assert(zip(StoppingPolicy
.longest
, cast(S
[]) null, new int[1]).length
== 1);
5945 assertThrown(zip(StoppingPolicy
.longest
, cast(S
[]) null, new int[1]).front
);
5948 // https://issues.dlang.org/show_bug.cgi?id=12007
5949 @nogc nothrow @safe pure unittest
5955 int front(){return 1;} @property
5956 R
save(){return this;} @property
5957 void opAssign(R
) @disable;
5961 assert(z
.save
== z
);
5964 nothrow pure @system unittest
5966 import std
.typecons
: tuple
;
5970 auto z1
= zip(refRange(&r1
), refRange(&r2
));
5973 assert(z1
.front
== tuple(1,2));
5974 assert(z2
.front
== tuple(0,1));
5977 @nogc nothrow pure @safe unittest
5979 // Test zip's `back` and `length` with non-equal ranges.
5980 static struct NonSliceableRandomAccess
5983 @property ref front()
5987 @property ref back()
5991 ref opIndex(size_t i
)
6005 return a
.moveFront();
6009 return a
.moveBack();
6011 auto moveAt(size_t i
)
6019 size_t
length() const
6028 static assert(isRandomAccessRange
!NonSliceableRandomAccess
);
6029 static assert(!hasSlicing
!NonSliceableRandomAccess
);
6030 static foreach (iteration
; 0 .. 2)
6032 int[5] data
= [101, 102, 103, 201, 202];
6033 static if (iteration
== 0)
6035 auto r1
= NonSliceableRandomAccess(data
[0 .. 3]);
6036 auto r2
= NonSliceableRandomAccess(data
[3 .. 5]);
6040 auto r1
= data
[0 .. 3];
6041 auto r2
= data
[3 .. 5];
6043 auto z
= zip(r1
, r2
);
6044 static assert(isRandomAccessRange
!(typeof(z
)));
6045 assert(z
.length
== 2);
6046 assert(z
.back
[0] == 102 && z
.back
[1] == 202);
6047 z
.back
= typeof(z
.back
)(-102, -202);// Assign to back.
6048 assert(z
.back
[0] == -102 && z
.back
[1] == -202);
6050 assert(z
.length
== 1);
6051 assert(z
.back
[0] == 101 && z
.back
[1] == 201);
6052 z
.front
= typeof(z
.front
)(-101, -201);
6053 assert(z
.moveBack() == typeof(z
.back
)(-101, -201));
6059 @nogc nothrow pure @safe unittest
6061 // Test opSlice on infinite `zip`.
6062 auto z
= zip(repeat(1), repeat(2));
6063 assert(hasSlicing
!(typeof(z
)));
6064 auto slice
= z
[10 .. 20];
6065 assert(slice
.length
== 10);
6066 static assert(!is(typeof(z
) == typeof(slice
)));
6070 Generate lockstep's opApply function as a mixin string.
6071 If withIndex is true prepend a size_t index to the delegate.
6073 private struct LockstepMixin(Ranges
...)
6075 import std
.conv
: text
;
6076 import std
.format
: format
;
6081 string
[] emptyChecks
;
6088 this(bool withIndex
, bool reverse
)
6097 size_t index
= ranges
[0].length
- 1;
6099 this.stoppingPolicy
== StoppingPolicy
.requireSameLength
,
6100 "Indexed lockstep can only be used with foreach_reverse when " ~
6101 "stoppingPolicy == requireSameLength");
6103 foreach (range
; ranges
[1 .. $])
6104 enforce(range
.length
== ranges
[0].length
);
6106 indexInc
= "--index;";
6110 indexDef
= "size_t index = 0;";
6111 indexInc
= "++index;";
6115 foreach (idx
, Range
; Ranges
)
6117 params
~= format("%sElementType!(Ranges[%s])", hasLvalueElements
!Range ?
"ref " : "", idx
);
6118 emptyChecks
~= format("!ranges[%s].empty", idx
);
6121 dgArgs
~= format("ranges[%s].back", idx
);
6122 popFronts
~= format("ranges[%s].popBack();", idx
);
6126 dgArgs
~= format("ranges[%s].front", idx
);
6127 popFronts
~= format("ranges[%s].popFront();", idx
);
6133 name
= "opApplyReverse";
6134 if (withIndex
) implName
= "opApplyReverseIdxImpl";
6135 else implName
= "opApplyReverseImpl";
6140 if (withIndex
) implName
= "opApplyIdxImpl";
6141 else implName
= "opApplyImpl";
6149 alias %s
= %s
!(int delegate(%-(%s
%|
, %)));
6151 name
, implName
, params
6158 int %s
(DG
)(scope DG dg
) scope
6160 import std
.exception
: enforce
;
6162 auto ranges
= this.ranges
;
6165 while (%-(%s
%|
&& %))
6167 if (int result
= dg(%-(%s
%|
, %))) return result
;
6173 if (this.stoppingPolicy
== StoppingPolicy
.requireSameLength
)
6175 foreach (range
; ranges
)
6176 enforce(range
.empty
);
6181 implName
, indexDef
, emptyChecks
, dgArgs
, popFronts
, indexInc
6187 Iterate multiple ranges in lockstep using a `foreach` loop. In contrast to
6188 $(LREF zip) it allows reference access to its elements. If only a single
6189 range is passed in, the `Lockstep` aliases itself away. If the
6190 ranges are of different lengths and `s` == `StoppingPolicy.shortest`
6191 stop after the shortest range is empty. If the ranges are of different
6192 lengths and `s` == `StoppingPolicy.requireSameLength`, throw an
6193 exception. `s` may not be `StoppingPolicy.longest`, and passing this
6194 will throw an exception.
6196 Iterating over `Lockstep` in reverse and with an index is only possible
6197 when `s` == `StoppingPolicy.requireSameLength`, in order to preserve
6198 indexes. If an attempt is made at iterating in reverse when `s` ==
6199 `StoppingPolicy.shortest`, an exception will be thrown.
6201 By default `StoppingPolicy` is set to `StoppingPolicy.shortest`.
6203 See_Also: $(LREF zip)
6205 `lockstep` is similar to $(LREF zip), but `zip` bundles its
6206 elements and returns a range.
6207 `lockstep` also supports reference access.
6208 Use `zip` if you want to pass the result to a range function.
6210 struct Lockstep(Ranges
...)
6211 if (Ranges
.length
> 1 && allSatisfy
!(isInputRange
, Ranges
))
6213 private Ranges ranges
;
6214 private StoppingPolicy stoppingPolicy
;
6217 this(Ranges ranges
, StoppingPolicy sp
= StoppingPolicy
.shortest
)
6219 import std
.exception
: enforce
;
6221 this.ranges
= ranges
;
6222 enforce(sp
!= StoppingPolicy
.longest
,
6223 "Can't use StoppingPolicy.Longest on Lockstep.");
6224 this.stoppingPolicy
= sp
;
6227 private enum lockstepMixinFF
= LockstepMixin
!Ranges(withIndex
: false, reverse
: false);
6228 mixin(lockstepMixinFF
.getImpl
);
6230 private enum lockstepMixinTF
= LockstepMixin
!Ranges(withIndex
: true, reverse
: false);
6231 mixin(lockstepMixinTF
.getImpl
);
6233 mixin(lockstepMixinFF
.getAlias
);
6234 mixin(lockstepMixinTF
.getAlias
);
6236 static if (allSatisfy
!(isBidirectionalRange
, Ranges
))
6238 private enum lockstepMixinFT
= LockstepMixin
!Ranges(withIndex
: false, reverse
: true);
6239 mixin(lockstepMixinFT
.getImpl
);
6240 static if (allSatisfy
!(hasLength
, Ranges
))
6242 private enum lockstepMixinTT
= LockstepMixin
!Ranges(withIndex
: true, reverse
: true);
6243 mixin(lockstepMixinTT
.getImpl
);
6244 mixin(lockstepMixinTT
.getAlias
);
6248 mixin(lockstepReverseFailMixin
!Ranges(withIndex
: true));
6249 alias opApplyReverse
= opApplyReverseIdxFail
;
6251 mixin(lockstepMixinFT
.getAlias
);
6255 mixin(lockstepReverseFailMixin
!Ranges(withIndex
: false));
6256 mixin(lockstepReverseFailMixin
!Ranges(withIndex
: true));
6257 alias opApplyReverse
= opApplyReverseFail
;
6258 alias opApplyReverse
= opApplyReverseIdxFail
;
6263 Lockstep
!(Ranges
) lockstep(Ranges
...)(Ranges ranges
)
6264 if (allSatisfy
!(isInputRange
, Ranges
))
6266 return Lockstep
!(Ranges
)(ranges
);
6269 Lockstep
!(Ranges
) lockstep(Ranges
...)(Ranges ranges
, StoppingPolicy s
)
6270 if (allSatisfy
!(isInputRange
, Ranges
))
6272 static if (Ranges
.length
> 1)
6273 return Lockstep
!Ranges(ranges
, s
);
6281 int[6] arr1
= [1,2,3,4,5,100];
6282 int[5] arr2
= [6,7,8,9,10];
6284 foreach (ref a
, b
; lockstep(arr1
[], arr2
[]))
6289 assert(arr1
== [7,9,11,13,15,100]);
6292 /// Lockstep also supports iterating with an index variable:
6295 int[3] arr1
= [1,2,3];
6296 int[3] arr2
= [4,5,6];
6298 foreach (index
, a
, b
; lockstep(arr1
[], arr2
[]))
6300 assert(arr1
[index
] == a
);
6301 assert(arr2
[index
] == b
);
6305 // https://issues.dlang.org/show_bug.cgi?id=15860: foreach_reverse on lockstep
6308 auto arr1
= [0, 1, 2, 3];
6309 auto arr2
= [4, 5, 6, 7];
6311 size_t n
= arr1
.length
- 1;
6312 foreach_reverse (index
, a
, b
; lockstep(arr1
, arr2
, StoppingPolicy
.requireSameLength
))
6316 assert(arr1
[index
] == a
);
6317 assert(arr2
[index
] == b
);
6323 foreach_reverse (a
, b
; lockstep(arr1
, arr3
))
6325 assert(a
== arr1
[$-n
] && b
== arr3
[$-n
]);
6332 import std
.algorithm
.iteration
: filter
;
6333 import std
.conv
: to
;
6335 // The filters are to make these the lowest common forward denominator ranges,
6336 // i.e. w/o ref return, random access, length, etc.
6337 auto foo
= filter
!"a"([1,2,3,4,5]);
6338 immutable bar
= [6f,7f,8f,9f,10f].idup
;
6339 auto l
= lockstep(foo
, bar
);
6341 // Should work twice. These are forward ranges with implicit save.
6347 foreach (a
, ref b
; l
)
6353 assert(res1
== [1,2,3,4,5]);
6354 assert(res2
== [6,7,8,9,10]);
6355 assert(bar
== [6f,7f,8f,9f,10f]);
6359 auto arr1
= [1,2,3,4,5];
6360 auto arr2
= [6,7,8,9,10];
6362 foreach (ref a
, ref b
; lockstep(arr1
, arr2
))
6367 assert(arr1
== [7,9,11,13,15]);
6369 // Make sure StoppingPolicy.requireSameLength doesn't throw.
6370 auto ls
= lockstep(arr1
, arr2
, StoppingPolicy
.requireSameLength
);
6379 // Make sure StoppingPolicy.requireSameLength throws.
6381 ls
= lockstep(arr1
, arr2
, StoppingPolicy
.requireSameLength
);
6384 foreach (a
, b
; ls
) {}
6386 } catch (Exception
) {}
6388 // Just make sure 1-range case instantiates. This hangs the compiler
6389 // when no explicit stopping policy is specified due to
6390 // https://issues.dlang.org/show_bug.cgi?id=4652
6391 auto stuff
= lockstep([1,2,3,4,5], StoppingPolicy
.shortest
);
6392 foreach (i
, a
; stuff
)
6394 assert(stuff
[i
] == a
);
6397 // Test with indexing.
6401 foreach (i
, a
, b
; lockstep(foo
, bar
))
6408 assert(indices
== to
!(size_t
[])([0, 1, 2, 3, 4]));
6409 assert(res1
== [1,2,3,4,5]);
6410 assert(res2
== [6f,7f,8f,9f,10f]);
6412 // Make sure we've worked around the relevant compiler bugs and this at least
6413 // compiles w/ >2 ranges.
6414 lockstep(foo
, foo
, foo
);
6416 // Make sure it works with const.
6417 const(int[])[] foo2
= [[1, 2, 3]];
6418 const(int[])[] bar2
= [[4, 5, 6]];
6419 auto c
= chain(foo2
, bar2
);
6421 foreach (f
, b
; lockstep(c
, c
)) {}
6424 foreach (x
, y
; lockstep(iota(0, 10), iota(0, 10))) { }
6432 @property bool empty() { return impl
.empty
; }
6433 @property int front() { return impl
[0]; } // N.B. non-ref
6434 void popFront() { impl
.popFront(); }
6436 auto data1
= [ 1, 2, 3, 4 ];
6437 auto data2
= [ 5, 6, 7, 8 ];
6438 auto r1
= RvalueRange(data1
);
6440 foreach (a
, ref b
; lockstep(r1
, r2
))
6445 assert(data1
== [ 1, 2, 3, 4 ]); // changes to a do not propagate to data
6446 assert(data2
== [ 6, 7, 8, 9 ]); // but changes to b do.
6448 // Since r1 is by-value only, the compiler should reject attempts to
6449 // foreach over it with ref.
6450 static assert(!__traits(compiles
, {
6451 foreach (ref a
, ref b
; lockstep(r1
, r2
)) { a
++; }
6455 private string
lockstepReverseFailMixin(Ranges
...)(bool withIndex
)
6457 import std
.format
: format
;
6463 message
= "Indexed reverse iteration with lockstep is only supported"
6464 ~"if all ranges are bidirectional and have a length.\n";
6468 message
= "Reverse iteration with lockstep is only supported if all ranges are bidirectional.\n";
6476 foreach (idx
, Range
; Ranges
)
6478 params
~= format("%sElementType!(Ranges[%s])", hasLvalueElements
!Range ?
"ref " : "", idx
);
6483 int opApplyReverse%sFail
()(scope int delegate(%s
) dg
)
6485 static assert(false, "%s");
6487 }, withIndex ?
"Idx" : "" , params
.join(", "), message
);
6490 // For generic programming, make sure Lockstep!(Range) is well defined for a
6492 template Lockstep(Range
)
6494 alias Lockstep
= Range
;
6498 Creates a mathematical sequence given the initial values and a
6499 recurrence function that computes the next value from the existing
6500 values. The sequence comes in the form of an infinite forward
6501 range. The type `Recurrence` itself is seldom used directly; most
6502 often, recurrences are obtained by calling the function $(D
6505 When calling `recurrence`, the function that computes the next
6506 value is specified as a template argument, and the initial values in
6507 the recurrence are passed as regular arguments. For example, in a
6508 Fibonacci sequence, there are two initial values (and therefore a
6509 state size of 2) because computing the next Fibonacci value needs the
6512 The signature of this function should be:
6514 auto fun(R)(R state, size_t n)
6516 where `n` will be the index of the current value, and `state` will be an
6517 opaque state vector that can be indexed with array-indexing notation
6518 `state[i]`, where valid values of `i` range from $(D (n - 1)) to
6519 $(D (n - State.length)).
6521 If the function is passed in string form, the state has name `"a"`
6522 and the zero-based index in the recurrence has name `"n"`. The
6523 given string must return the desired value for `a[n]` given
6524 `a[n - 1]`, `a[n - 2]`, `a[n - 3]`,..., `a[n - stateSize]`. The
6525 state size is dictated by the number of arguments passed to the call
6526 to `recurrence`. The `Recurrence` struct itself takes care of
6527 managing the recurrence's state and shifting it appropriately.
6529 struct Recurrence(alias fun
, StateType
, size_t stateSize
)
6531 import std
.functional
: binaryFun
;
6533 StateType
[stateSize
] _state
;
6536 this(StateType
[stateSize
] initial
) { _state
= initial
; }
6540 static auto trustedCycle(ref typeof(_state
) s
) @trusted
6544 // The cast here is reasonable because fun may cause integer
6545 // promotion, but needs to return a StateType to make its operation
6546 // closed. Therefore, we have no other choice.
6547 _state
[_n
% stateSize
] = cast(StateType
) binaryFun
!(fun
, "a", "n")(
6548 trustedCycle(_state
), _n
+ stateSize
);
6552 @property StateType
front()
6554 return _state
[_n
% stateSize
];
6557 @property typeof(this) save()
6562 enum bool empty
= false;
6566 pure @safe nothrow unittest
6568 import std
.algorithm
.comparison
: equal
;
6570 // The Fibonacci numbers, using function in string form:
6571 // a[0] = 1, a[1] = 1, and compute a[n+1] = a[n-1] + a[n]
6572 auto fib
= recurrence
!("a[n-1] + a[n-2]")(1, 1);
6573 assert(fib
.take(10).equal([1, 1, 2, 3, 5, 8, 13, 21, 34, 55]));
6575 // The factorials, using function in lambda form:
6576 auto fac
= recurrence
!((a
,n
) => a
[n
-1] * n
)(1);
6577 assert(take(fac
, 10).equal([
6578 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880
6581 // The triangular numbers, using function in explicit form:
6582 static size_t
genTriangular(R
)(R state
, size_t n
)
6584 return state
[n
-1] + n
;
6586 auto tri
= recurrence
!genTriangular(0);
6587 assert(take(tri
, 10).equal([0, 1, 3, 6, 10, 15, 21, 28, 36, 45]));
6591 Recurrence
!(fun
, CommonType
!(State
), State
.length
)
6592 recurrence(alias fun
, State
...)(State initial
)
6594 CommonType
!(State
)[State
.length
] state
;
6595 foreach (i
, Unused
; State
)
6597 state
[i
] = initial
[i
];
6599 return typeof(return)(state
);
6602 pure @safe nothrow unittest
6604 import std
.algorithm
.comparison
: equal
;
6606 auto fib
= recurrence
!("a[n-1] + a[n-2]")(1, 1);
6607 static assert(isForwardRange
!(typeof(fib
)));
6609 int[] witness
= [1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ];
6610 assert(equal(take(fib
, 10), witness
));
6611 foreach (e
; take(fib
, 10)) {}
6612 auto fact
= recurrence
!("n * a[n-1]")(1);
6613 assert( equal(take(fact
, 10), [1, 1, 2, 2*3, 2*3*4, 2*3*4*5, 2*3*4*5*6,
6614 2*3*4*5*6*7, 2*3*4*5*6*7*8, 2*3*4*5*6*7*8*9][]) );
6615 auto piapprox
= recurrence
!("a[n] + (n & 1 ? 4.0 : -4.0) / (2 * n + 3)")(4.0);
6616 foreach (e
; take(piapprox
, 20)) {}
6617 // Thanks to yebblies for this test and the associated fix
6618 auto r
= recurrence
!"a[n-2]"(1, 2);
6619 witness
= [1, 2, 1, 2, 1];
6620 assert(equal(take(r
, 5), witness
));
6624 `Sequence` is similar to `Recurrence` except that iteration is
6625 presented in the so-called $(HTTP en.wikipedia.org/wiki/Closed_form,
6626 closed form). This means that the `n`th element in the series is
6627 computable directly from the initial values and `n` itself. This
6628 implies that the interface offered by `Sequence` is a random-access
6629 range, as opposed to the regular `Recurrence`, which only offers
6632 The state of the sequence is stored as a `Tuple` so it can be
6635 struct Sequence(alias fun
, State
)
6638 import std
.functional
: binaryFun
;
6640 alias compute
= binaryFun
!(fun
, "a", "n");
6641 alias ElementType
= typeof(compute(State
.init
, cast(size_t
) 1));
6645 static struct DollarToken
{}
6648 this(State initial
, size_t n
= 0)
6654 @property ElementType
front()
6656 return compute(_state
, _n
);
6664 enum opDollar
= DollarToken();
6666 auto opSlice(size_t lower
, size_t upper
)
6671 "Attempting to slice a Sequence with a larger first argument than the second."
6676 return typeof(this)(_state
, _n
+ lower
).take(upper
- lower
);
6679 auto opSlice(size_t lower
, DollarToken
)
6681 return typeof(this)(_state
, _n
+ lower
);
6684 ElementType
opIndex(size_t n
)
6686 return compute(_state
, n
+ _n
);
6689 enum bool empty
= false;
6691 @property Sequence
save() { return this; }
6695 auto sequence(alias fun
, State
...)(State args
)
6697 import std
.typecons
: Tuple
, tuple
;
6698 alias Return
= Sequence
!(fun
, Tuple
!State
);
6699 return Return(tuple(args
));
6702 /// Odd numbers, using function in string form:
6703 pure @safe nothrow @nogc unittest
6705 auto odds
= sequence
!("a[0] + n * a[1]")(1, 2);
6706 assert(odds
.front
== 1);
6708 assert(odds
.front
== 3);
6710 assert(odds
.front
== 5);
6713 /// Triangular numbers, using function in lambda form:
6714 pure @safe nothrow @nogc unittest
6716 auto tri
= sequence
!((a
,n
) => n
*(n
+1)/2)();
6718 // Note random access
6719 assert(tri
[0] == 0);
6720 assert(tri
[3] == 6);
6721 assert(tri
[1] == 1);
6722 assert(tri
[4] == 10);
6723 assert(tri
[2] == 3);
6726 /// Fibonacci numbers, using function in explicit form:
6727 @safe nothrow @nogc unittest
6729 import std
.math
.exponential
: pow
;
6730 import std
.math
.rounding
: round
;
6731 import std
.math
.algebraic
: sqrt
;
6732 static ulong computeFib(S
)(S state
, size_t n
)
6735 return cast(ulong)(round((pow(state
[0], n
+1) - pow(state
[1], n
+1)) /
6738 auto fib
= sequence
!computeFib(
6739 (1.0 + sqrt(5.0)) / 2.0, // Golden Ratio
6740 (1.0 - sqrt(5.0)) / 2.0, // Conjugate of Golden Ratio
6743 // Note random access with [] operator
6744 assert(fib
[1] == 1);
6745 assert(fib
[4] == 5);
6746 assert(fib
[3] == 3);
6747 assert(fib
[2] == 2);
6748 assert(fib
[9] == 55);
6751 pure @safe nothrow @nogc unittest
6753 import std
.typecons
: Tuple
, tuple
;
6754 auto y
= Sequence
!("a[0] + n * a[1]", Tuple
!(int, int))(tuple(0, 4));
6755 static assert(isForwardRange
!(typeof(y
)));
6758 //auto y = sequence!("a[0] + n * a[1]")(0, 4);
6759 //foreach (e; take(y, 15))
6762 auto odds
= Sequence
!("a[0] + n * a[1]", Tuple
!(int, int))(
6764 for (int currentOdd
= 1; currentOdd
<= 21; currentOdd
+= 2)
6766 assert(odds
.front
== odds
[0]);
6767 assert(odds
[0] == currentOdd
);
6772 pure @safe nothrow @nogc unittest
6774 import std
.algorithm
.comparison
: equal
;
6776 auto odds
= sequence
!("a[0] + n * a[1]")(1, 2);
6777 static assert(hasSlicing
!(typeof(odds
)));
6779 //Note: don't use drop or take as the target of an equal,
6780 //since they'll both just forward to opSlice, making the tests irrelevant
6782 // static slicing tests
6783 assert(equal(odds
[0 .. 5], only(1, 3, 5, 7, 9)));
6784 assert(equal(odds
[3 .. 7], only(7, 9, 11, 13)));
6786 // relative slicing test, testing slicing is NOT agnostic of state
6787 auto odds_less5
= odds
.drop(5); //this should actually call odds[5 .. $]
6788 assert(equal(odds_less5
[0 .. 3], only(11, 13, 15)));
6789 assert(equal(odds_less5
[0 .. 10], odds
[5 .. 15]));
6791 //Infinite slicing tests
6792 odds
= odds
[10 .. $];
6793 assert(equal(odds
.take(3), only(21, 23, 25)));
6796 // https://issues.dlang.org/show_bug.cgi?id=5036
6797 pure @safe nothrow unittest
6799 auto s
= sequence
!((a
, n
) => new int)(0);
6800 assert(s
.front
!= s
.front
); // no caching
6805 Creates a range of values that span the given starting and stopping
6809 begin = The starting value.
6810 end = The value that serves as the stopping criterion. This value is not
6811 included in the range.
6812 step = The value to add to the current value at each iteration.
6815 A range that goes through the numbers `begin`, $(D begin + step),
6816 $(D begin + 2 * step), `...`, up to and excluding `end`.
6818 The two-argument overloads have $(D step = 1). If $(D begin < end && step <
6819 0) or $(D begin > end && step > 0) or $(D begin == end), then an empty range
6820 is returned. If $(D step == 0) then $(D begin == end) is an error.
6822 For built-in types, the range returned is a random access range. For
6823 user-defined types that support `++`, the range is an input
6826 An integral iota also supports `in` operator from the right. It takes
6827 the stepping into account, the integral won't be considered
6828 contained if it falls between two consecutive values of the range.
6829 `contains` does the same as in, but from lefthand side.
6837 // The following groups all produce the same output of:
6844 import std.range : iota;
6845 foreach (i; iota(0, 5))
6849 writefln("%(%s %|%)", iota(0, 5));
6851 import std.algorithm.iteration : map;
6852 import std.algorithm.mutation : copy;
6854 iota(0, 5).map!(i => format("%s ", i)).copy(stdout.lockingTextWriter());
6859 auto iota(B
, E
, S
)(B begin
, E end
, S step
)
6860 if ((isIntegral
!(CommonType
!(B
, E
)) || isPointer
!(CommonType
!(B
, E
)))
6863 import std
.conv
: unsigned
;
6865 alias Value
= CommonType
!(Unqual
!B
, Unqual
!E
);
6866 alias StepType
= Unqual
!S
;
6868 assert(step
!= 0 || begin
== end
);
6870 static struct Result
6872 private Value current
, last
;
6873 private StepType step
; // by convention, 0 if range is empty
6875 this(Value current
, Value pastLast
, StepType step
)
6877 if (current
< pastLast
&& step
> 0)
6880 assert(unsigned((pastLast
- current
) / step
) <= size_t
.max
);
6881 // Cast below can't fail because current < pastLast
6882 this.last
= cast(Value
) (pastLast
- 1);
6883 this.last
-= unsigned(this.last
- current
) % step
;
6885 else if (current
> pastLast
&& step
< 0)
6887 // Iterating downward
6888 assert(unsigned((current
- pastLast
) / (0 - step
)) <= size_t
.max
);
6889 // Cast below can't fail because current > pastLast
6890 this.last
= cast(Value
) (pastLast
+ 1);
6891 this.last
+= unsigned(current
- this.last
) % (0 - step
);
6895 // Initialize an empty range
6900 this.current
= current
;
6903 @property bool empty() const { return step
== 0; }
6904 @property inout(Value
) front() inout { assert(!empty
); return current
; }
6908 if (current
== last
) step
= 0;
6909 else current
+= step
;
6912 @property inout(Value
) back() inout
6920 if (current
== last
) step
= 0;
6924 @property auto save() { return this; }
6926 inout(Value
) opIndex(ulong n
) inout
6928 assert(n
< this.length
);
6930 // Just cast to Value here because doing so gives overflow behavior
6931 // consistent with calling popFront() n times.
6932 return cast(inout Value
) (current
+ step
* n
);
6934 auto opBinaryRight(string op
)(Value val
) const
6937 if (empty
) return false;
6938 //cast to avoid becoming unsigned
6939 auto supposedIndex
= cast(StepType
)(val
- current
) / step
;
6940 return supposedIndex
< length
&& supposedIndex
* step
+ current
== val
;
6942 auto contains(Value x
){return x
in this;}
6943 inout(Result
) opSlice() inout { return this; }
6944 inout(Result
) opSlice(ulong lower
, ulong upper
) inout
6946 assert(upper
>= lower
&& upper
<= this.length
);
6948 return cast(inout Result
) Result(
6949 cast(Value
)(current
+ lower
* step
),
6950 cast(Value
)(current
+ upper
* step
),
6953 @property size_t
length() const
6956 return 1 + cast(size_t
) (unsigned(last
- current
) / step
);
6958 return 1 + cast(size_t
) (unsigned(current
- last
) / (0 - step
));
6962 alias opDollar
= length
;
6965 return Result(begin
, end
, step
);
6969 auto iota(B
, E
)(B begin
, E end
)
6970 if (isFloatingPoint
!(CommonType
!(B
, E
)))
6972 return iota(begin
, end
, CommonType
!(B
, E
)(1));
6976 auto iota(B
, E
)(B begin
, E end
)
6977 if (isIntegral
!(CommonType
!(B
, E
)) || isPointer
!(CommonType
!(B
, E
)))
6979 import std
.conv
: unsigned
;
6981 alias Value
= CommonType
!(Unqual
!B
, Unqual
!E
);
6983 static struct Result
6985 private Value current
, pastLast
;
6987 this(Value current
, Value pastLast
)
6989 if (current
< pastLast
)
6991 assert(unsigned(pastLast
- current
) <= size_t
.max
,
6992 "`iota` range is too long");
6994 this.current
= current
;
6995 this.pastLast
= pastLast
;
6999 // Initialize an empty range
7000 this.current
= this.pastLast
= current
;
7004 @property bool empty() const { return current
== pastLast
; }
7005 @property inout(Value
) front() inout
7007 assert(!empty
, "Attempt to access `front` of empty `iota` range");
7012 assert(!empty
, "Attempt to `popFront` of empty `iota` range");
7016 @property inout(Value
) back() inout
7018 assert(!empty
, "Attempt to access `back` of empty `iota` range");
7019 return cast(inout(Value
))(pastLast
- 1);
7023 assert(!empty
, "Attempt to `popBack` of empty `iota` range");
7027 @property auto save() { return this; }
7029 inout(Value
) opIndex(size_t n
) inout
7031 assert(n
< this.length
,
7032 "Attempt to read out-of-bounds index of `iota` range");
7034 // Just cast to Value here because doing so gives overflow behavior
7035 // consistent with calling popFront() n times.
7036 return cast(inout Value
) (current
+ n
);
7038 auto opBinaryRight(string op
)(Value val
) const
7041 return current
<= val
&& val
< pastLast
;
7043 auto contains(Value x
){return x
in this;}
7044 inout(Result
) opSlice() inout { return this; }
7045 inout(Result
) opSlice(ulong lower
, ulong upper
) inout
7047 assert(upper
>= lower
&& upper
<= this.length
,
7048 "Attempt to get out-of-bounds slice of `iota` range");
7050 return cast(inout Result
) Result(cast(Value
)(current
+ lower
),
7051 cast(Value
)(pastLast
- (length
- upper
)));
7053 @property size_t
length() const
7055 return cast(size_t
)(pastLast
- current
);
7058 alias opDollar
= length
;
7061 return Result(begin
, end
);
7066 if (is(typeof(iota(E(0), end
))))
7069 return iota(begin
, end
);
7073 // Specialization for floating-point types
7074 auto iota(B
, E
, S
)(B begin
, E end
, S step
)
7075 if (isFloatingPoint
!(CommonType
!(B
, E
, S
)))
7078 assert(step
!= 0, "iota: step must not be 0");
7079 assert((end
- begin
) / step
>= 0, "iota: incorrect startup parameters");
7083 alias Value
= Unqual
!(CommonType
!(B
, E
, S
));
7084 static struct Result
7086 private Value start
, step
;
7087 private size_t index
, count
;
7089 this(Value start
, Value end
, Value step
)
7091 import std
.conv
: to
;
7095 immutable fcount
= (end
- start
) / step
;
7096 count
= to
!size_t(fcount
);
7097 auto pastEnd
= start
+ count
* step
;
7100 if (pastEnd
< end
) ++count
;
7101 assert(start
+ count
* step
>= end
);
7105 if (pastEnd
> end
) ++count
;
7106 assert(start
+ count
* step
<= end
);
7110 @property bool empty() const { return index
== count
; }
7111 @property Value
front() const { assert(!empty
); return start
+ step
* index
; }
7117 @property Value
back() const
7120 return start
+ step
* (count
- 1);
7128 @property auto save() { return this; }
7130 Value
opIndex(size_t n
) const
7133 return start
+ step
* (n
+ index
);
7135 inout(Result
) opSlice() inout
7139 inout(Result
) opSlice(size_t lower
, size_t upper
) inout
7141 assert(upper
>= lower
&& upper
<= count
);
7145 ret.count
= upper
- lower
+ ret.index
;
7146 return cast(inout Result
) ret;
7148 @property size_t
length() const
7150 return count
- index
;
7153 alias opDollar
= length
;
7156 return Result(begin
, end
, step
);
7162 import std
.algorithm
.comparison
: equal
;
7163 import std
.math
.operations
: isClose
;
7165 auto r
= iota(0, 10, 1);
7166 assert(equal(r
, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
7167 assert(equal(r
, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
7169 assert(r
.contains(3)); //Same as above
7173 assert(equal(r
, [0, 3, 6, 9]));
7176 auto rf
= iota(0.0, 0.5, 0.1);
7177 assert(isClose(rf
, [0.0, 0.1, 0.2, 0.3, 0.4]));
7180 pure nothrow @nogc @safe unittest
7182 import std
.traits
: Signed
;
7183 //float overloads use std.conv.to so can't be @nogc or nothrow
7184 alias ssize_t
= Signed
!size_t
;
7185 assert(iota(ssize_t
.max
, 0, -1).length
== ssize_t
.max
);
7186 assert(iota(ssize_t
.max
, ssize_t
.min
, -1).length
== size_t
.max
);
7187 assert(iota(ssize_t
.max
, ssize_t
.min
, -2).length
== 1 + size_t
.max
/ 2);
7188 assert(iota(ssize_t
.min
, ssize_t
.max
, 2).length
== 1 + size_t
.max
/ 2);
7189 assert(iota(ssize_t
.max
, ssize_t
.min
, -3).length
== size_t
.max
/ 3);
7192 debug @system unittest
7193 {//check the contracts
7194 import core
.exception
: AssertError
;
7195 import std
.exception
: assertThrown
;
7196 assertThrown
!AssertError(iota(1,2,0));
7197 assertThrown
!AssertError(iota(0f,1f,0f));
7198 assertThrown
!AssertError(iota(1f,0f,0.1f));
7199 assertThrown
!AssertError(iota(0f,1f,-0.1f));
7202 pure @system nothrow unittest
7204 int[] a
= [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
7205 auto r1
= iota(a
.ptr
, a
.ptr
+ a
.length
, 1);
7206 assert(r1
.front
== a
.ptr
);
7207 assert(r1
.back
== a
.ptr
+ a
.length
- 1);
7208 assert(&a
[4] in r1
);
7211 pure @safe nothrow @nogc unittest
7213 assert(iota(1UL, 0UL).length
== 0);
7214 assert(iota(1UL, 0UL, 1).length
== 0);
7215 assert(iota(0, 1, 1).length
== 1);
7216 assert(iota(1, 0, -1).length
== 1);
7217 assert(iota(0, 1, -1).length
== 0);
7218 assert(iota(ulong.max
, 0).length
== 0);
7223 import std
.algorithm
.comparison
: equal
;
7224 import std
.algorithm
.searching
: count
;
7225 import std
.math
.operations
: isClose
, nextUp
, nextDown
;
7226 import std
.meta
: AliasSeq
;
7228 static assert(is(ElementType
!(typeof(iota(0f))) == float));
7230 static assert(hasLength
!(typeof(iota(0, 2))));
7231 auto r
= iota(0, 10, 1);
7232 assert(r
[$ - 1] == 9);
7233 assert(equal(r
, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][]));
7235 auto rSlice
= r
[2 .. 8];
7236 assert(equal(rSlice
, [2, 3, 4, 5, 6, 7]));
7239 assert(rSlice
[0] == rSlice
.front
);
7240 assert(rSlice
.front
== 3);
7243 assert(rSlice
[rSlice
.length
- 1] == rSlice
.back
);
7244 assert(rSlice
.back
== 6);
7247 assert(equal(rSlice
, [0, 1, 2, 3]));
7248 assert(3 in rSlice
);
7249 assert(!(4 in rSlice
));
7252 assert(equal(rr
, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][]));
7254 r
= iota(0, -10, -1);
7255 assert(equal(r
, [0, -1, -2, -3, -4, -5, -6, -7, -8, -9][]));
7257 assert(equal(rSlice
, [-3, -4, -5, -6, -7, -8]));
7259 r
= iota(0, -6, -3);
7260 assert(equal(r
, [0, -3][]));
7262 assert(equal(rSlice
, [-3]));
7264 r
= iota(0, -7, -3);
7265 assert(equal(r
, [0, -3, -6][]));
7269 assert(equal(rSlice
, [-3, -6]));
7270 assert(!(0 in rSlice
));
7271 assert(!(-2 in rSlice
));
7272 assert(!(-5 in rSlice
));
7273 assert(!(3 in rSlice
));
7274 assert(!(-9 in rSlice
));
7277 assert(equal(r
, [0, 3, 6, 9][]));
7280 assert(equal(rSlice
, [3, 6]));
7282 auto rf
= iota(0.0, 0.5, 0.1);
7283 assert(isClose(rf
, [0.0, 0.1, 0.2, 0.3, 0.4][]));
7284 assert(rf
.length
== 5);
7287 assert(rf
.length
== 4);
7289 auto rfSlice
= rf
[1 .. 4];
7290 assert(rfSlice
.length
== 3);
7291 assert(isClose(rfSlice
, [0.2, 0.3, 0.4]));
7294 assert(isClose(rfSlice
[0], 0.3));
7297 assert(rf
.length
== 3);
7299 rfSlice
= rf
[1 .. 3];
7300 assert(rfSlice
.length
== 2);
7301 assert(isClose(rfSlice
, [0.3, 0.4]));
7302 assert(isClose(rfSlice
[0], 0.3));
7304 // With something just above 0.5
7305 rf
= iota(0.0, nextUp(0.5), 0.1);
7306 assert(isClose(rf
, [0.0, 0.1, 0.2, 0.3, 0.4, 0.5][]));
7308 assert(rf
[rf
.length
- 1] == rf
.back
);
7309 assert(isClose(rf
.back
, 0.4));
7310 assert(rf
.length
== 5);
7313 rf
= iota(0.0, -0.5, -0.1);
7314 assert(isClose(rf
, [0.0, -0.1, -0.2, -0.3, -0.4][]));
7315 rfSlice
= rf
[2 .. 5];
7316 assert(isClose(rfSlice
, [-0.2, -0.3, -0.4]));
7318 rf
= iota(0.0, nextDown(-0.5), -0.1);
7319 assert(isClose(rf
, [0.0, -0.1, -0.2, -0.3, -0.4, -0.5][]));
7322 auto rl
= iota(5_000_000L);
7323 assert(rl
.length
== 5_000_000L);
7325 assert(4_000_000L in rl
);
7326 assert(!(-4_000_000L in rl
));
7327 assert(!(5_000_000L in rl
));
7329 // iota of longs with steps
7330 auto iota_of_longs_with_steps
= iota(50L, 101L, 10);
7331 assert(iota_of_longs_with_steps
.length
== 6);
7332 assert(equal(iota_of_longs_with_steps
, [50L, 60L, 70L, 80L, 90L, 100L]));
7334 // iota of unsigned zero length (https://issues.dlang.org/show_bug.cgi?id=6222)
7335 // Actually trying to consume it is the only way to find something is wrong
7336 // because the public properties are all correct.
7337 auto iota_zero_unsigned
= iota(0, 0u, 3);
7338 assert(count(iota_zero_unsigned
) == 0);
7340 // https://issues.dlang.org/show_bug.cgi?id=7982
7341 // unsigned reverse iota can be buggy if `.length` doesn't
7342 // take them into account
7343 assert(iota(10u, 0u, -1).length
== 10);
7344 assert(iota(10u, 0u, -2).length
== 5);
7345 assert(iota(uint.max
, uint.max
-10, -1).length
== 10);
7346 assert(iota(uint.max
, uint.max
-10, -2).length
== 5);
7347 assert(iota(uint.max
, 0u, -1).length
== uint.max
);
7349 assert(20 in iota(20u, 10u, -2));
7350 assert(16 in iota(20u, 10u, -2));
7351 assert(!(15 in iota(20u, 10u, -2)));
7352 assert(!(10 in iota(20u, 10u, -2)));
7353 assert(!(uint.max
in iota(20u, 10u, -1)));
7354 assert(!(int.min
in iota(20u, 10u, -1)));
7355 assert(!(int.max
in iota(20u, 10u, -1)));
7358 // https://issues.dlang.org/show_bug.cgi?id=8920
7359 static foreach (Type
; AliasSeq
!(byte, ubyte, short, ushort,
7360 int, uint, long, ulong))
7363 foreach (i
; iota(cast(Type
) 0, cast(Type
) 10)) { val
++; }
7368 pure @safe nothrow unittest
7370 import std
.algorithm
.mutation
: copy
;
7371 auto idx
= new size_t
[100];
7372 copy(iota(0, idx
.length
), idx
);
7377 import std
.meta
: AliasSeq
;
7378 static foreach (range
; AliasSeq
!(iota(2, 27, 4),
7380 iota(2.7, 12.3, .1),
7383 const cRange
= range
;
7384 const e
= cRange
.empty
;
7385 const f
= cRange
.front
;
7386 const b
= cRange
.back
;
7387 const i
= cRange
[2];
7388 const s1
= cRange
[];
7389 const s2
= cRange
[0 .. 3];
7390 const l
= cRange
.length
;
7396 //The ptr stuff can't be done at compile time, so we unfortunately end
7397 //up with some code duplication here.
7398 auto arr
= [0, 5, 3, 5, 5, 7, 9, 2, 0, 42, 7, 6];
7401 const cRange
= iota(arr
.ptr
, arr
.ptr
+ arr
.length
, 3);
7402 const e
= cRange
.empty
;
7403 const f
= cRange
.front
;
7404 const b
= cRange
.back
;
7405 const i
= cRange
[2];
7406 const s1
= cRange
[];
7407 const s2
= cRange
[0 .. 3];
7408 const l
= cRange
.length
;
7412 const cRange
= iota(arr
.ptr
, arr
.ptr
+ arr
.length
);
7413 const e
= cRange
.empty
;
7414 const f
= cRange
.front
;
7415 const b
= cRange
.back
;
7416 const i
= cRange
[2];
7417 const s1
= cRange
[];
7418 const s2
= cRange
[0 .. 3];
7419 const l
= cRange
.length
;
7423 @nogc nothrow pure @safe unittest
7426 ushort start
= 0, end
= 10, step
= 2;
7427 foreach (i
; iota(start
, end
, step
))
7428 static assert(is(typeof(i
) == ushort));
7431 ubyte start
= 0, end
= 255, step
= 128;
7433 foreach (i
; iota(start
, end
, step
))
7435 static assert(is(typeof(i
) == ubyte));
7442 /* Generic overload that handles arbitrary types that support arithmetic
7445 * User-defined types such as $(REF BigInt, std,bigint) are also supported, as long
7446 * as they can be incremented with `++` and compared with `<` or `==`.
7449 auto iota(B
, E
)(B begin
, E end
)
7450 if (!isIntegral
!(CommonType
!(B
, E
)) &&
7451 !isFloatingPoint
!(CommonType
!(B
, E
)) &&
7452 !isPointer
!(CommonType
!(B
, E
)) &&
7453 is(typeof((ref B b
) { ++b
; })) &&
7454 (is(typeof(B
.init
< E
.init
)) ||
is(typeof(B
.init
== E
.init
))) )
7456 static struct Result
7461 @property bool empty()
7463 static if (is(typeof(B
.init
< E
.init
)))
7464 return !(current
< end
);
7465 else static if (is(typeof(B
.init
!= E
.init
)))
7466 return current
== end
;
7470 @property auto front() { return current
; }
7476 @property auto save() { return this; }
7478 return Result(begin
, end
);
7483 import std
.algorithm
.comparison
: equal
;
7485 // Test iota() for a type that only supports ++ and != but does not have
7487 struct Cyclic(int wrapAround
)
7491 this(int start
) { current
= start
% wrapAround
; }
7493 bool opEquals(Cyclic c
) const { return current
== c
.current
; }
7494 bool opEquals(int i
) const { return current
== i
; }
7495 void opUnary(string op
)()
7498 current
= (current
+ 1) % wrapAround
;
7501 alias Cycle5
= Cyclic
!5;
7504 auto i1
= iota(Cycle5(1), Cycle5(4));
7505 assert(i1
.equal([1, 2, 3]));
7508 auto i2
= iota(Cycle5(3), Cycle5(2));
7509 assert(i2
.equal([3, 4, 0, 1 ]));
7512 // https://issues.dlang.org/show_bug.cgi?id=23453
7515 auto r
= iota('a', 'z');
7516 static assert(isForwardRange
!(typeof(r
)));
7520 Options for the $(LREF FrontTransversal) and $(LREF Transversal) ranges
7523 enum TransverseOptions
7526 When transversed, the elements of a range of ranges are assumed to
7527 have different lengths (e.g. a jagged array).
7529 assumeJagged
, //default
7531 The transversal enforces that the elements of a range of ranges have
7532 all the same length (e.g. an array of arrays, all having the same
7533 length). Checking is done once upon construction of the transversal
7538 The transversal assumes, without verifying, that the elements of a
7539 range of ranges have all the same length. This option is useful if
7540 checking was already done from the outside of the range.
7548 import std
.algorithm
.comparison
: equal
;
7549 import std
.exception
: assertThrown
;
7551 auto arr
= [[1, 2], [3, 4, 5]];
7553 auto r1
= arr
.frontTransversal
!(TransverseOptions
.assumeJagged
);
7554 assert(r1
.equal([1, 3]));
7556 // throws on construction
7557 assertThrown
!Exception(arr
.frontTransversal
!(TransverseOptions
.enforceNotJagged
));
7559 auto r2
= arr
.frontTransversal
!(TransverseOptions
.assumeNotJagged
);
7560 assert(r2
.equal([1, 3]));
7562 // either assuming or checking for equal lengths makes
7563 // the result a random access range
7565 static assert(!__traits(compiles
, r1
[0]));
7569 Given a range of ranges, iterate transversally through the first
7570 elements of each of the enclosed ranges.
7572 struct FrontTransversal(Ror
,
7573 TransverseOptions opt
= TransverseOptions
.assumeJagged
)
7575 alias RangeOfRanges
= Unqual
!(Ror
);
7576 alias RangeType
= .ElementType
!RangeOfRanges
;
7577 alias ElementType
= .ElementType
!RangeType
;
7579 private void prime()
7581 static if (opt
== TransverseOptions
.assumeJagged
)
7583 while (!_input
.empty
&& _input
.front
.empty
)
7587 static if (isBidirectionalRange
!RangeOfRanges
)
7589 while (!_input
.empty
&& _input
.back
.empty
)
7598 Construction from an input.
7600 this(RangeOfRanges input
)
7604 static if (opt
== TransverseOptions
.enforceNotJagged
)
7605 // (isRandomAccessRange!RangeOfRanges
7606 // && hasLength!RangeType)
7608 import std
.exception
: enforce
;
7611 immutable commonLength
= _input
.front
.length
;
7614 enforce(e
.length
== commonLength
);
7620 Forward range primitives.
7622 static if (isInfinite
!RangeOfRanges
)
7624 enum bool empty
= false;
7628 @property bool empty()
7630 static if (opt
!= TransverseOptions
.assumeJagged
)
7633 return _input
.front
.empty
;
7636 return _input
.empty
;
7641 @property auto ref front()
7643 assert(!empty
, "Attempting to fetch the front of an empty FrontTransversal");
7644 return _input
.front
.front
;
7648 static if (hasMobileElements
!RangeType
)
7650 ElementType
moveFront()
7652 return _input
.front
.moveFront();
7656 static if (hasAssignableElements
!RangeType
)
7658 @property void front(ElementType val
)
7660 import core
.lifetime
: forward
;
7662 // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542
7663 _input
.front
.front
= __ctfe ? val
: forward
!val
;
7670 assert(!empty
, "Attempting to popFront an empty FrontTransversal");
7676 Duplicates this `frontTransversal`. Note that only the encapsulating
7677 range of range will be duplicated. Underlying ranges will not be
7680 static if (isForwardRange
!RangeOfRanges
)
7682 @property FrontTransversal
save()
7684 return FrontTransversal(_input
.save
);
7688 static if (isBidirectionalRange
!RangeOfRanges
)
7691 Bidirectional primitives. They are offered if $(D
7692 isBidirectionalRange!RangeOfRanges).
7694 @property auto ref back()
7696 assert(!empty
, "Attempting to fetch the back of an empty FrontTransversal");
7697 return _input
.back
.front
;
7702 assert(!empty
, "Attempting to popBack an empty FrontTransversal");
7708 static if (hasMobileElements
!RangeType
)
7710 ElementType
moveBack()
7712 return _input
.back
.moveFront();
7716 static if (hasAssignableElements
!RangeType
)
7718 @property void back(ElementType val
)
7720 import core
.lifetime
: forward
;
7722 // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542
7723 _input
.back
.front
= __ctfe ? val
: forward
!val
;
7728 static if (isRandomAccessRange
!RangeOfRanges
&&
7729 (opt
== TransverseOptions
.assumeNotJagged ||
7730 opt
== TransverseOptions
.enforceNotJagged
))
7733 Random-access primitive. It is offered if $(D
7734 isRandomAccessRange!RangeOfRanges && (opt ==
7735 TransverseOptions.assumeNotJagged || opt ==
7736 TransverseOptions.enforceNotJagged)).
7738 auto ref opIndex(size_t n
)
7740 return _input
[n
].front
;
7744 static if (hasMobileElements
!RangeType
)
7746 ElementType
moveAt(size_t n
)
7748 return _input
[n
].moveFront();
7752 static if (hasAssignableElements
!RangeType
)
7754 void opIndexAssign(ElementType val
, size_t n
)
7756 import core
.lifetime
: forward
;
7758 // __ctfe check is workaround for https://issues.dlang.org/show_bug.cgi?id=21542
7759 _input
[n
].front
= __ctfe ? val
: forward
!val
;
7762 mixin ImplementLength
!_input
;
7765 Slicing if offered if `RangeOfRanges` supports slicing and all the
7766 conditions for supporting indexing are met.
7768 static if (hasSlicing
!RangeOfRanges
)
7770 typeof(this) opSlice(size_t lower
, size_t upper
)
7772 return typeof(this)(_input
[lower
.. upper
]);
7777 auto opSlice() { return this; }
7780 RangeOfRanges _input
;
7784 FrontTransversal
!(RangeOfRanges
, opt
) frontTransversal(
7785 TransverseOptions opt
= TransverseOptions
.assumeJagged
,
7789 return typeof(return)(rr
);
7793 pure @safe nothrow unittest
7795 import std
.algorithm
.comparison
: equal
;
7796 int[][] x
= new int[][2];
7799 auto ror = frontTransversal(x
);
7800 assert(equal(ror, [ 1, 3 ][]));
7805 import std
.algorithm
.comparison
: equal
;
7806 import std
.internal
.test.dummyrange
: AllDummyRanges
, DummyRange
, ReturnBy
;
7808 static assert(is(FrontTransversal
!(immutable int[][])));
7810 foreach (DummyType
; AllDummyRanges
)
7813 [DummyType
.init
, DummyType
.init
, DummyType
.init
, DummyType
.init
];
7815 foreach (i
, ref elem
; dummies
)
7817 // Just violate the DummyRange abstraction to get what I want.
7818 elem
.arr
= elem
.arr
[i
..$ - (3 - i
)];
7821 auto ft
= frontTransversal
!(TransverseOptions
.assumeNotJagged
)(dummies
);
7822 static if (isForwardRange
!DummyType
)
7824 static assert(isForwardRange
!(typeof(ft
)));
7827 assert(equal(ft
, [1, 2, 3, 4]));
7830 assert(equal(ft
[0 .. 2], [1, 2]));
7831 assert(equal(ft
[1 .. 3], [2, 3]));
7833 assert(ft
.front
== ft
.moveFront());
7834 assert(ft
.back
== ft
.moveBack());
7835 assert(ft
.moveAt(1) == ft
[1]);
7838 // Test infiniteness propagation.
7839 static assert(isInfinite
!(typeof(frontTransversal(repeat("foo")))));
7841 static if (DummyType
.r
== ReturnBy
.Reference
)
7845 scope(exit
) ft
.front
--;
7846 assert(dummies
.front
.front
== 2);
7851 scope(exit
) ft
.front
= 1;
7852 assert(dummies
[0].front
== 5);
7857 scope(exit
) ft
.back
= 4;
7858 assert(dummies
.back
.front
== 88);
7863 scope(exit
) ft
[1] = 2;
7864 assert(dummies
[1].front
== 99);
7870 // https://issues.dlang.org/show_bug.cgi?id=16363
7871 pure @safe nothrow unittest
7873 import std
.algorithm
.comparison
: equal
;
7875 int[][] darr
= [[0, 1], [4, 5]];
7876 auto ft
= frontTransversal
!(TransverseOptions
.assumeNotJagged
)(darr
);
7878 assert(equal(ft
, [0, 4]));
7879 static assert(isRandomAccessRange
!(typeof(ft
)));
7882 // https://issues.dlang.org/show_bug.cgi?id=16442
7883 pure @safe nothrow unittest
7885 int[][] arr
= [[], []];
7887 auto ft
= frontTransversal
!(TransverseOptions
.assumeNotJagged
)(arr
);
7894 int[][] arr
= [[], []];
7896 auto ft
= frontTransversal
!(TransverseOptions
.enforceNotJagged
)(arr
);
7900 // https://issues.dlang.org/show_bug.cgi?id=24481
7907 void opAssign()(auto ref const(typeof(this)) that
) const { called
= true; }
7910 const(Handle
)[][] arr
= [[Handle(0), Handle(10)],
7911 [Handle(1), Handle(11)],
7912 [Handle(2), Handle(12)],
7913 [Handle(3), Handle(13)],
7914 [Handle(4), Handle(14)]];
7917 auto range
= arr
.frontTransversal();
7920 range
.front
= Handle(42);
7921 assert(called
== true);
7924 range
.back
= Handle(42);
7925 assert(called
== true);
7928 auto range
= arr
.frontTransversal
!(TransverseOptions
.assumeNotJagged
)();
7931 range
.front
= Handle(42);
7932 assert(called
== true);
7935 range
.back
= Handle(42);
7936 assert(called
== true);
7939 range
[0] = Handle(42);
7940 assert(called
== true);
7945 Given a range of ranges, iterate transversally through the
7946 `n`th element of each of the enclosed ranges. This function
7947 is similar to `unzip` in other languages.
7950 opt = Controls the assumptions the function makes about the lengths
7952 rr = An input range of random access ranges
7954 At minimum, an input range. Range primitives such as bidirectionality
7955 and random access are given if the element type of `rr` provides them.
7957 struct Transversal(Ror
,
7958 TransverseOptions opt
= TransverseOptions
.assumeJagged
)
7960 private alias RangeOfRanges
= Unqual
!Ror
;
7961 private alias InnerRange
= ElementType
!RangeOfRanges
;
7962 private alias E
= ElementType
!InnerRange
;
7964 private void prime()
7966 static if (opt
== TransverseOptions
.assumeJagged
)
7968 while (!_input
.empty
&& _input
.front
.length
<= _n
)
7972 static if (isBidirectionalRange
!RangeOfRanges
)
7974 while (!_input
.empty
&& _input
.back
.length
<= _n
)
7983 Construction from an input and an index.
7985 this(RangeOfRanges input
, size_t n
)
7990 static if (opt
== TransverseOptions
.enforceNotJagged
)
7992 import std
.exception
: enforce
;
7995 immutable commonLength
= _input
.front
.length
;
7998 enforce(e
.length
== commonLength
);
8004 Forward range primitives.
8006 static if (isInfinite
!(RangeOfRanges
))
8008 enum bool empty
= false;
8012 @property bool empty()
8014 return _input
.empty
;
8019 @property auto ref front()
8021 assert(!empty
, "Attempting to fetch the front of an empty Transversal");
8022 return _input
.front
[_n
];
8026 static if (hasMobileElements
!InnerRange
)
8030 return _input
.front
.moveAt(_n
);
8035 static if (hasAssignableElements
!InnerRange
)
8037 @property void front(E val
)
8039 _input
.front
[_n
] = val
;
8047 assert(!empty
, "Attempting to popFront an empty Transversal");
8053 static if (isForwardRange
!RangeOfRanges
)
8055 @property typeof(this) save()
8058 ret._input
= _input
.save
;
8063 static if (isBidirectionalRange
!RangeOfRanges
)
8066 Bidirectional primitives. They are offered if $(D
8067 isBidirectionalRange!RangeOfRanges).
8069 @property auto ref back()
8071 assert(!empty
, "Attempting to fetch the back of an empty Transversal");
8072 return _input
.back
[_n
];
8078 assert(!empty
, "Attempting to popBack an empty Transversal");
8084 static if (hasMobileElements
!InnerRange
)
8088 return _input
.back
.moveAt(_n
);
8093 static if (hasAssignableElements
!InnerRange
)
8095 @property void back(E val
)
8097 _input
.back
[_n
] = val
;
8103 static if (isRandomAccessRange
!RangeOfRanges
&&
8104 (opt
== TransverseOptions
.assumeNotJagged ||
8105 opt
== TransverseOptions
.enforceNotJagged
))
8108 Random-access primitive. It is offered if $(D
8109 isRandomAccessRange!RangeOfRanges && (opt ==
8110 TransverseOptions.assumeNotJagged || opt ==
8111 TransverseOptions.enforceNotJagged)).
8113 auto ref opIndex(size_t n
)
8115 return _input
[n
][_n
];
8119 static if (hasMobileElements
!InnerRange
)
8123 return _input
[n
].moveAt(_n
);
8128 static if (hasAssignableElements
!InnerRange
)
8130 void opIndexAssign(E val
, size_t n
)
8132 _input
[n
][_n
] = val
;
8136 mixin ImplementLength
!_input
;
8139 Slicing if offered if `RangeOfRanges` supports slicing and all the
8140 conditions for supporting indexing are met.
8142 static if (hasSlicing
!RangeOfRanges
)
8144 typeof(this) opSlice(size_t lower
, size_t upper
)
8146 return typeof(this)(_input
[lower
.. upper
], _n
);
8151 auto opSlice() { return this; }
8154 RangeOfRanges _input
;
8159 Transversal
!(RangeOfRanges
, opt
) transversal
8160 (TransverseOptions opt
= TransverseOptions
.assumeJagged
, RangeOfRanges
)
8161 (RangeOfRanges rr
, size_t n
)
8163 return typeof(return)(rr
, n
);
8169 import std
.algorithm
.comparison
: equal
;
8170 int[][] x
= new int[][2];
8173 auto ror = transversal(x
, 1);
8174 assert(equal(ror, [ 2, 4 ]));
8177 /// The following code does a full unzip
8180 import std
.algorithm
.comparison
: equal
;
8181 import std
.algorithm
.iteration
: map
;
8182 int[][] y
= [[1, 2, 3], [4, 5, 6]];
8183 auto z
= y
.front
.walkLength
.iota
.map
!(i
=> transversal(y
, i
));
8184 assert(equal
!equal(z
, [[1, 4], [2, 5], [3, 6]]));
8189 import std
.internal
.test.dummyrange
: DummyRange
, Length
, RangeType
, ReturnBy
;
8191 int[][] x
= new int[][2];
8194 auto ror = transversal
!(TransverseOptions
.assumeNotJagged
)(x
, 1);
8195 auto witness
= [ 2, 4 ];
8197 foreach (e
; ror) assert(e
== witness
[i
++]);
8199 assert(ror.length
== 2);
8201 static assert(is(Transversal
!(immutable int[][])));
8203 // Make sure ref, assign is being propagated.
8206 scope(exit
) ror.front
--;
8207 assert(x
[0][1] == 3);
8211 scope(exit
) ror.front
= 2;
8212 assert(x
[0][1] == 5);
8213 assert(ror.moveFront() == 5);
8217 scope(exit
) ror.back
= 4;
8218 assert(x
[1][1] == 999);
8219 assert(ror.moveBack() == 999);
8223 scope(exit
) ror[0] = 2;
8224 assert(x
[0][1] == 999);
8225 assert(ror.moveAt(0) == 999);
8228 // Test w/o ref return.
8229 alias D
= DummyRange
!(ReturnBy
.Value
, Length
.Yes
, RangeType
.Random
);
8230 auto drs
= [D
.init
, D
.init
];
8231 foreach (num
; 0 .. 10)
8233 auto t
= transversal
!(TransverseOptions
.enforceNotJagged
)(drs
, num
);
8234 assert(t
[0] == t
[1]);
8235 assert(t
[1] == num
+ 1);
8238 static assert(isInfinite
!(typeof(transversal(repeat([1,2,3]), 1))));
8241 auto mat
= [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]];
8242 auto mat1
= transversal
!(TransverseOptions
.assumeNotJagged
)(mat
, 1)[1 .. 3];
8243 assert(mat1
[0] == 6);
8244 assert(mat1
[1] == 10);
8247 struct Transposed(RangeOfRanges
,
8248 TransverseOptions opt
= TransverseOptions
.assumeJagged
)
8249 if (isForwardRange
!RangeOfRanges
&&
8250 isInputRange
!(ElementType
!RangeOfRanges
) &&
8251 hasAssignableElements
!RangeOfRanges
)
8253 this(RangeOfRanges input
)
8255 this._input
= input
;
8256 static if (opt
== TransverseOptions
.enforceNotJagged
)
8258 import std
.exception
: enforce
;
8261 immutable commonLength
= _input
.front
.length
;
8264 enforce(e
.length
== commonLength
);
8269 @property auto front()
8271 import std
.algorithm
.iteration
: filter
, map
;
8273 .filter
!(a
=> !a
.empty
)
8274 .map
!(a
=> a
.front
);
8279 // Advance the position of each subrange.
8280 auto r
= _input
.save
;
8294 static if (isRandomAccessRange
!(ElementType
!RangeOfRanges
))
8296 auto ref opIndex(size_t n
)
8298 return transversal
!opt(_input
, n
);
8302 @property bool empty()
8304 if (_input
.empty
) return true;
8305 foreach (e
; _input
.save
)
8307 if (!e
.empty
) return false;
8312 auto opSlice() { return this; }
8315 RangeOfRanges _input
;
8320 // Boundary case: transpose of empty range should be empty
8322 assert(transposed(ror).empty
);
8325 // https://issues.dlang.org/show_bug.cgi?id=9507
8328 import std
.algorithm
.comparison
: equal
;
8330 auto r
= [[1,2], [3], [4,5], [], [6]];
8331 assert(r
.transposed
.equal
!equal([
8337 // https://issues.dlang.org/show_bug.cgi?id=17742
8340 import std
.algorithm
.iteration
: map
;
8341 import std
.algorithm
.comparison
: equal
;
8342 auto ror = 5.iota
.map
!(y
=> 5.iota
.map
!(x
=> x
* y
).array
).array
;
8343 assert(ror[3][2] == 6);
8344 auto result
= transposed
!(TransverseOptions
.assumeNotJagged
)(ror);
8345 assert(result
[2][3] == 6);
8347 auto x
= [[1,2,3],[4,5,6]];
8348 auto y
= transposed
!(TransverseOptions
.assumeNotJagged
)(x
);
8349 assert(y
.front
.equal([1,4]));
8350 assert(y
[0].equal([1,4]));
8351 assert(y
[0][0] == 1);
8352 assert(y
[1].equal([2,5]));
8353 assert(y
[1][1] == 5);
8355 auto yy
= transposed
!(TransverseOptions
.enforceNotJagged
)(x
);
8356 assert(yy
.front
.equal([1,4]));
8357 assert(yy
[0].equal([1,4]));
8358 assert(yy
[0][0] == 1);
8359 assert(yy
[1].equal([2,5]));
8360 assert(yy
[1][1] == 5);
8362 auto z
= x
.transposed
; // assumeJagged
8363 assert(z
.front
.equal([1,4]));
8364 assert(z
[0].equal([1,4]));
8365 assert(!is(typeof(z
[0][0])));
8370 import std
.exception
: assertThrown
;
8372 auto r
= [[1,2], [3], [4,5], [], [6]];
8373 assertThrown(r
.transposed
!(TransverseOptions
.enforceNotJagged
));
8377 Given a range of ranges, returns a range of ranges where the $(I i)'th subrange
8378 contains the $(I i)'th elements of the original subranges.
8381 opt = Controls the assumptions the function makes about the lengths of the ranges (i.e. jagged or not)
8382 rr = Range of ranges
8384 Transposed
!(RangeOfRanges
, opt
) transposed
8385 (TransverseOptions opt
= TransverseOptions
.assumeJagged
, RangeOfRanges
)
8387 if (isForwardRange
!RangeOfRanges
&&
8388 isInputRange
!(ElementType
!RangeOfRanges
) &&
8389 hasAssignableElements
!RangeOfRanges
)
8391 return Transposed
!(RangeOfRanges
, opt
)(rr
);
8397 import std
.algorithm
.comparison
: equal
;
8402 auto xp
= transposed(ror);
8403 assert(equal
!"a.equal(b)"(xp
, [
8413 int[][] x
= new int[][2];
8416 auto tr
= transposed(x
);
8417 int[][] witness
= [ [ 1, 3 ], [ 2, 4 ] ];
8422 assert(array(e
) == witness
[i
++]);
8426 // https://issues.dlang.org/show_bug.cgi?id=8764
8429 import std
.algorithm
.comparison
: equal
;
8430 ulong[] t0
= [ 123 ];
8432 assert(!hasAssignableElements
!(typeof(t0
[].chunks(1))));
8433 assert(!is(typeof(transposed(t0
[].chunks(1)))));
8434 assert(is(typeof(transposed(t0
[].chunks(1).array()))));
8436 auto t1
= transposed(t0
[].chunks(1).array());
8437 assert(equal
!"a.equal(b)"(t1
, [[123]]));
8441 This struct takes two ranges, `source` and `indices`, and creates a view
8442 of `source` as if its elements were reordered according to `indices`.
8443 `indices` may include only a subset of the elements of `source` and
8444 may also repeat elements.
8446 `Source` must be a random access range. The returned range will be
8447 bidirectional or random-access if `Indices` is bidirectional or
8448 random-access, respectively.
8450 struct Indexed(Source
, Indices
)
8451 if (isRandomAccessRange
!Source
&& isInputRange
!Indices
&&
8452 is(typeof(Source
.init
[ElementType
!(Indices
).init
])))
8454 this(Source source
, Indices indices
)
8456 this._source
= source
;
8457 this._indices
= indices
;
8460 /// Range primitives
8461 @property auto ref front()
8463 assert(!empty
, "Attempting to fetch the front of an empty Indexed");
8464 return _source
[_indices
.front
];
8470 assert(!empty
, "Attempting to popFront an empty Indexed");
8471 _indices
.popFront();
8474 static if (isInfinite
!Indices
)
8476 enum bool empty
= false;
8481 @property bool empty()
8483 return _indices
.empty
;
8487 static if (isForwardRange
!Indices
)
8490 @property typeof(this) save()
8492 // Don't need to save _source because it's never consumed.
8493 return typeof(this)(_source
, _indices
.save
);
8498 static if (hasAssignableElements
!Source
)
8500 @property auto ref front(ElementType
!Source newVal
)
8503 return _source
[_indices
.front
] = newVal
;
8508 static if (hasMobileElements
!Source
)
8514 return _source
.moveAt(_indices
.front
);
8518 static if (isBidirectionalRange
!Indices
)
8521 @property auto ref back()
8523 assert(!empty
, "Attempting to fetch the back of an empty Indexed");
8524 return _source
[_indices
.back
];
8530 assert(!empty
, "Attempting to popBack an empty Indexed");
8535 static if (hasAssignableElements
!Source
)
8537 @property auto ref back(ElementType
!Source newVal
)
8540 return _source
[_indices
.back
] = newVal
;
8545 static if (hasMobileElements
!Source
)
8551 return _source
.moveAt(_indices
.back
);
8556 mixin ImplementLength
!_indices
;
8558 static if (isRandomAccessRange
!Indices
)
8561 auto ref opIndex(size_t index
)
8563 return _source
[_indices
[index
]];
8566 static if (hasSlicing
!Indices
)
8569 typeof(this) opSlice(size_t a
, size_t b
)
8571 return typeof(this)(_source
, _indices
[a
.. b
]);
8576 static if (hasAssignableElements
!Source
)
8579 auto opIndexAssign(ElementType
!Source newVal
, size_t index
)
8581 return _source
[_indices
[index
]] = newVal
;
8586 static if (hasMobileElements
!Source
)
8589 auto moveAt(size_t index
)
8591 return _source
.moveAt(_indices
[index
]);
8596 // All this stuff is useful if someone wants to index an Indexed
8597 // without adding a layer of indirection.
8600 Returns the source range.
8602 @property Source
source()
8608 Returns the indices range.
8610 @property Indices
indices()
8615 static if (isRandomAccessRange
!Indices
)
8618 Returns the physical index into the source range corresponding to a
8619 given logical index. This is useful, for example, when indexing
8620 an `Indexed` without adding another layer of indirection.
8622 size_t
physicalIndex(size_t logicalIndex
)
8624 return _indices
[logicalIndex
];
8630 auto ind
= indexed([1, 2, 3, 4, 5], [1, 3, 4]);
8631 assert(ind
.physicalIndex(0) == 1);
8642 Indexed
!(Source
, Indices
) indexed(Source
, Indices
)(Source source
, Indices indices
)
8644 return typeof(return)(source
, indices
);
8650 import std
.algorithm
.comparison
: equal
;
8651 auto source
= [1, 2, 3, 4, 5];
8652 auto indices
= [4, 3, 1, 2, 0, 4];
8653 auto ind
= indexed(source
, indices
);
8654 assert(equal(ind
, [5, 4, 2, 3, 1, 5]));
8655 assert(equal(retro(ind
), [5, 1, 3, 2, 4, 5]));
8661 auto ind
= indexed([1, 2, 3, 4, 5], [1, 3, 4]);
8662 assert(ind
.physicalIndex(0) == 1);
8665 auto source
= [1, 2, 3, 4, 5];
8666 auto indices
= [4, 3, 1, 2, 0, 4];
8667 auto ind
= indexed(source
, indices
);
8669 // When elements of indices are duplicated and Source has lvalue elements,
8670 // these are aliased in ind.
8672 assert(ind
[0] == 6);
8673 assert(ind
[5] == 6);
8678 import std
.internal
.test.dummyrange
: AllDummyRanges
, propagatesLength
,
8679 propagatesRangeType
, RangeType
;
8681 foreach (DummyType
; AllDummyRanges
)
8683 auto d
= DummyType
.init
;
8684 auto r
= indexed([1, 2, 3, 4, 5], d
);
8685 static assert(propagatesRangeType
!(DummyType
, typeof(r
)));
8686 static assert(propagatesLength
!(DummyType
, typeof(r
)));
8691 This range iterates over fixed-sized chunks of size `chunkSize` of a
8692 `source` range. `Source` must be an $(REF_ALTTEXT input range, isInputRange, std,range,primitives).
8693 `chunkSize` must be greater than zero.
8695 If `!isInfinite!Source` and `source.walkLength` is not evenly
8696 divisible by `chunkSize`, the back element of this range will contain
8697 fewer than `chunkSize` elements.
8699 If `Source` is a forward range, the resulting range will be forward ranges as
8700 well. Otherwise, the resulting chunks will be input ranges consuming the same
8701 input: iterating over `front` will shrink the chunk such that subsequent
8702 invocations of `front` will no longer return the full chunk, and calling
8703 `popFront` on the outer range will invalidate any lingering references to
8704 previous values of `front`.
8707 source = Range from which the chunks will be selected
8708 chunkSize = Chunk size
8710 See_Also: $(LREF slide)
8712 Returns: Range of chunks.
8714 struct Chunks(Source
)
8715 if (isInputRange
!Source
)
8717 static if (isForwardRange
!Source
)
8719 /// Standard constructor
8720 this(Source source
, size_t chunkSize
)
8722 assert(chunkSize
!= 0, "Cannot create a Chunk with an empty chunkSize");
8724 _chunkSize
= chunkSize
;
8727 /// Input range primitives. Always present.
8728 @property auto front()
8730 assert(!empty
, "Attempting to fetch the front of an empty Chunks");
8731 return _source
.save
.take(_chunkSize
);
8737 assert(!empty
, "Attempting to popFront and empty Chunks");
8738 _source
.popFrontN(_chunkSize
);
8741 static if (!isInfinite
!Source
)
8743 @property bool empty()
8745 return _source
.empty
;
8751 /// Forward range primitives. Only present if `Source` is a forward range.
8752 @property typeof(this) save()
8754 return typeof(this)(_source
.save
, _chunkSize
);
8757 static if (hasLength
!Source
)
8759 /// Length. Only if `hasLength!Source` is `true`
8760 @property size_t
length()
8762 // Note: _source.length + _chunkSize may actually overflow.
8763 // We cast to ulong to mitigate the problem on x86 machines.
8764 // For x64 machines, we just suppose we'll never overflow.
8765 // The "safe" code would require either an extra branch, or a
8766 // modulo operation, which is too expensive for such a rare case
8767 return cast(size_t
)((cast(ulong)(_source
.length
) + _chunkSize
- 1) / _chunkSize
);
8769 //Note: No point in defining opDollar here without slicing.
8770 //opDollar is defined below in the hasSlicing!Source section
8773 static if (hasSlicing
!Source
)
8775 //Used for various purposes
8776 private enum hasSliceToEnd
= is(typeof(Source
.init
[_chunkSize
.. $]) == Source
);
8779 Indexing and slicing operations. Provided only if
8780 `hasSlicing!Source` is `true`.
8782 auto opIndex(size_t index
)
8784 immutable start
= index
* _chunkSize
;
8785 immutable end
= start
+ _chunkSize
;
8787 static if (isInfinite
!Source
)
8788 return _source
[start
.. end
];
8791 import std
.algorithm
.comparison
: min
;
8792 immutable len
= _source
.length
;
8793 assert(start
< len
, "chunks index out of bounds");
8794 return _source
[start
.. min(end
, len
)];
8799 static if (hasLength
!Source
)
8800 typeof(this) opSlice(size_t lower
, size_t upper
)
8802 import std
.algorithm
.comparison
: min
;
8803 assert(lower
<= upper
&& upper
<= length
, "chunks slicing index out of bounds");
8804 immutable len
= _source
.length
;
8805 return chunks(_source
[min(lower
* _chunkSize
, len
) .. min(upper
* _chunkSize
, len
)], _chunkSize
);
8807 else static if (hasSliceToEnd
)
8808 //For slicing an infinite chunk, we need to slice the source to the end.
8809 typeof(takeExactly(this, 0)) opSlice(size_t lower
, size_t upper
)
8811 assert(lower
<= upper
, "chunks slicing index out of bounds");
8812 return chunks(_source
[lower
* _chunkSize
.. $], _chunkSize
).takeExactly(upper
- lower
);
8815 static if (isInfinite
!Source
)
8817 static if (hasSliceToEnd
)
8819 private static struct DollarToken
{}
8820 DollarToken
opDollar()
8822 return DollarToken();
8825 typeof(this) opSlice(size_t lower
, DollarToken
)
8827 return typeof(this)(_source
[lower
* _chunkSize
.. $], _chunkSize
);
8833 //Dollar token carries a static type, with no extra information.
8834 //It can lazily transform into _source.length on algorithmic
8835 //operations such as : chunks[$/2, $-1];
8836 private static struct DollarToken
8839 @property size_t
momLength()
8843 alias momLength
this;
8845 DollarToken
opDollar()
8847 return DollarToken(&this);
8850 //Slice overloads optimized for using dollar. Without this, to slice to end, we would...
8851 //1. Evaluate chunks.length
8852 //2. Multiply by _chunksSize
8853 //3. To finally just compare it (with min) to the original length of source (!)
8854 //These overloads avoid that.
8855 typeof(this) opSlice(DollarToken
, DollarToken
)
8857 static if (hasSliceToEnd
)
8858 return chunks(_source
[$ .. $], _chunkSize
);
8861 immutable len
= _source
.length
;
8862 return chunks(_source
[len
.. len
], _chunkSize
);
8865 typeof(this) opSlice(size_t lower
, DollarToken
)
8867 import std
.algorithm
.comparison
: min
;
8868 assert(lower
<= length
, "chunks slicing index out of bounds");
8869 static if (hasSliceToEnd
)
8870 return chunks(_source
[min(lower
* _chunkSize
, _source
.length
) .. $], _chunkSize
);
8873 immutable len
= _source
.length
;
8874 return chunks(_source
[min(lower
* _chunkSize
, len
) .. len
], _chunkSize
);
8877 typeof(this) opSlice(DollarToken
, size_t upper
)
8879 assert(upper
== length
, "chunks slicing index out of bounds");
8880 return this[$ .. $];
8885 //Bidirectional range primitives
8886 static if (hasSlicing
!Source
&& hasLength
!Source
)
8889 Bidirectional range primitives. Provided only if both
8890 `hasSlicing!Source` and `hasLength!Source` are `true`.
8892 @property auto back()
8894 assert(!empty
, "back called on empty chunks");
8895 immutable len
= _source
.length
;
8896 immutable start
= (len
- 1) / _chunkSize
* _chunkSize
;
8897 return _source
[start
.. len
];
8903 assert(!empty
, "popBack() called on empty chunks");
8904 immutable end
= (_source
.length
- 1) / _chunkSize
* _chunkSize
;
8905 _source
= _source
[0 .. end
];
8913 else // is input range only
8915 import std
.typecons
: RefCounted
;
8919 private RefCounted
!Impl impl
;
8921 @property bool empty() { return impl
.curSizeLeft
== 0 || impl
.r
.empty
; }
8922 @property auto front() { return impl
.r
.front
; }
8925 assert(impl
.curSizeLeft
> 0 && !impl
.r
.empty
);
8934 private size_t chunkSize
;
8935 private size_t curSizeLeft
;
8938 private RefCounted
!Impl impl
;
8940 private this(Source r
, size_t chunkSize
)
8942 impl
= RefCounted
!Impl(r
, r
.empty ?
0 : chunkSize
, chunkSize
);
8945 @property bool empty() { return impl
.chunkSize
== 0; }
8946 @property Chunk
front() return { return Chunk(impl
); }
8950 impl
.curSizeLeft
-= impl
.r
.popFrontN(impl
.curSizeLeft
);
8952 impl
.curSizeLeft
= impl
.chunkSize
;
8957 static assert(isInputRange
!(typeof(this)));
8962 Chunks
!Source
chunks(Source
)(Source source
, size_t chunkSize
)
8963 if (isInputRange
!Source
)
8965 return typeof(return)(source
, chunkSize
);
8971 import std
.algorithm
.comparison
: equal
;
8972 auto source
= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
8973 auto chunks
= chunks(source
, 4);
8974 assert(chunks
[0] == [1, 2, 3, 4]);
8975 assert(chunks
[1] == [5, 6, 7, 8]);
8976 assert(chunks
[2] == [9, 10]);
8977 assert(chunks
.back
== chunks
[2]);
8978 assert(chunks
.front
== chunks
[0]);
8979 assert(chunks
.length
== 3);
8980 assert(equal(retro(array(chunks
)), array(retro(chunks
))));
8983 /// Non-forward input ranges are supported, but with limited semantics.
8984 @system /*@safe*/ unittest // FIXME: can't be @safe because RefCounted isn't.
8986 import std
.algorithm
.comparison
: equal
;
8990 // The generator doesn't save state, so it cannot be a forward range.
8991 auto inputRange
= generate
!(() => ++i
).take(10);
8993 // We can still process it in chunks, but it will be single-pass only.
8994 auto chunked
= inputRange
.chunks(2);
8996 assert(chunked
.front
.equal([1, 2]));
8997 assert(chunked
.front
.empty
); // Iterating the chunk has consumed it
8999 assert(chunked
.front
.equal([3, 4]));
9002 @system /*@safe*/ unittest
9004 import std
.algorithm
.comparison
: equal
;
9005 import std
.internal
.test.dummyrange
: ReferenceInputRange
;
9007 auto data
= [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
9008 auto r
= new ReferenceInputRange
!int(data
).chunks(3);
9009 assert(r
.equal
!equal([
9016 auto data2
= [ 1, 2, 3, 4, 5, 6 ];
9017 auto r2
= new ReferenceInputRange
!int(data2
).chunks(3);
9018 assert(r2
.equal
!equal([
9023 auto data3
= [ 1, 2, 3, 4, 5 ];
9024 auto r3
= new ReferenceInputRange
!int(data3
).chunks(2);
9025 assert(r3
.front
.equal([1, 2]));
9029 assert(r3
.front
.equal([5]));
9034 auto source
= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
9035 auto chunks
= chunks(source
, 4);
9036 auto chunks2
= chunks
.save
;
9038 assert(chunks
[0] == [5, 6, 7, 8]);
9039 assert(chunks
[1] == [9, 10]);
9041 assert(chunks2
[1] == [5, 6, 7, 8]);
9042 assert(chunks2
.length
== 2);
9044 static assert(isRandomAccessRange
!(typeof(chunks
)));
9049 import std
.algorithm
.comparison
: equal
;
9051 //Extra toying with slicing and indexing.
9052 auto chunks1
= [0, 0, 1, 1, 2, 2, 3, 3, 4].chunks(2);
9053 auto chunks2
= [0, 0, 1, 1, 2, 2, 3, 3, 4, 4].chunks(2);
9055 assert(chunks1
.length
== 5);
9056 assert(chunks2
.length
== 5);
9057 assert(chunks1
[4] == [4]);
9058 assert(chunks2
[4] == [4, 4]);
9059 assert(chunks1
.back
== [4]);
9060 assert(chunks2
.back
== [4, 4]);
9062 assert(chunks1
[0 .. 1].equal([[0, 0]]));
9063 assert(chunks1
[0 .. 2].equal([[0, 0], [1, 1]]));
9064 assert(chunks1
[4 .. 5].equal([[4]]));
9065 assert(chunks2
[4 .. 5].equal([[4, 4]]));
9067 assert(chunks1
[0 .. 0].equal((int[][]).init
));
9068 assert(chunks1
[5 .. 5].equal((int[][]).init
));
9069 assert(chunks2
[5 .. 5].equal((int[][]).init
));
9072 assert(chunks1
[$ .. $].equal((int[][]).init
)); //Quick
9073 assert(chunks2
[$ .. $].equal((int[][]).init
)); //Quick
9074 assert(chunks1
[$ - 1 .. $].equal([[4]])); //Semiquick
9075 assert(chunks2
[$ - 1 .. $].equal([[4, 4]])); //Semiquick
9076 assert(chunks1
[$ .. 5].equal((int[][]).init
)); //Semiquick
9077 assert(chunks2
[$ .. 5].equal((int[][]).init
)); //Semiquick
9079 assert(chunks1
[$ / 2 .. $ - 1].equal([[2, 2], [3, 3]])); //Slow
9084 import std
.algorithm
.comparison
: equal
;
9085 import std
.algorithm
.iteration
: filter
;
9088 auto r
= filter
!"true"([1, 2, 3, 4, 5]).chunks(2);
9089 assert(equal
!"equal(a, b)"(r
, [[1, 2], [3, 4], [5]]));
9091 //InfiniteRange w/o RA
9092 auto fibsByPairs
= recurrence
!"a[n-1] + a[n-2]"(1, 1).chunks(2);
9093 assert(equal
!`equal(a, b)`(fibsByPairs
.take(2), [[ 1, 1], [ 2, 3]]));
9095 //InfiniteRange w/ RA and slicing
9096 auto odds
= sequence
!("a[0] + n * a[1]")(1, 2);
9097 auto oddsByPairs
= odds
.chunks(2);
9098 assert(equal
!`equal(a, b)`(oddsByPairs
.take(2), [[ 1, 3], [ 5, 7]]));
9100 //Requires phobos#991 for Sequence to have slice to end
9101 static assert(hasSlicing
!(typeof(odds
)));
9102 assert(equal
!`equal(a, b)`(oddsByPairs
[3 .. 5], [[13, 15], [17, 19]]));
9103 assert(equal
!`equal(a, b)`(oddsByPairs
[3 .. $].take(2), [[13, 15], [17, 19]]));
9109 This range splits a `source` range into `chunkCount` chunks of
9110 approximately equal length. `Source` must be a forward range with
9113 Unlike $(LREF chunks), `evenChunks` takes a chunk count (not size).
9114 The returned range will contain zero or more $(D source.length /
9115 chunkCount + 1) elements followed by $(D source.length / chunkCount)
9116 elements. If $(D source.length < chunkCount), some chunks will be empty.
9118 `chunkCount` must not be zero, unless `source` is also empty.
9120 struct EvenChunks(Source
)
9121 if (isForwardRange
!Source
&& hasLength
!Source
)
9123 /// Standard constructor
9124 this(Source source
, size_t chunkCount
)
9126 assert(chunkCount
!= 0 || source
.empty
, "Cannot create EvenChunks with a zero chunkCount");
9128 _chunkCount
= chunkCount
;
9131 /// Forward range primitives. Always present.
9132 @property auto front()
9134 assert(!empty
, "Attempting to fetch the front of an empty evenChunks");
9135 return _source
.save
.take(_chunkPos(1));
9141 assert(!empty
, "Attempting to popFront an empty evenChunks");
9142 _source
.popFrontN(_chunkPos(1));
9147 @property bool empty()
9149 return _chunkCount
== 0;
9153 @property typeof(this) save()
9155 return typeof(this)(_source
.save
, _chunkCount
);
9159 @property size_t
length() const
9163 //Note: No point in defining opDollar here without slicing.
9164 //opDollar is defined below in the hasSlicing!Source section
9166 static if (hasSlicing
!Source
)
9169 Indexing, slicing and bidirectional operations and range primitives.
9170 Provided only if `hasSlicing!Source` is `true`.
9172 auto opIndex(size_t index
)
9174 assert(index
< _chunkCount
, "evenChunks index out of bounds");
9175 return _source
[_chunkPos(index
) .. _chunkPos(index
+1)];
9179 typeof(this) opSlice(size_t lower
, size_t upper
)
9181 assert(lower
<= upper
&& upper
<= length
, "evenChunks slicing index out of bounds");
9182 return evenChunks(_source
[_chunkPos(lower
) .. _chunkPos(upper
)], upper
- lower
);
9186 @property auto back()
9188 assert(!empty
, "back called on empty evenChunks");
9189 return _source
[_chunkPos(_chunkCount
- 1) .. _source
.length
];
9195 assert(!empty
, "popBack() called on empty evenChunks");
9196 _source
= _source
[0 .. _chunkPos(_chunkCount
- 1)];
9205 size_t
_chunkPos(size_t i
)
9208 _chunkCount = 5, _source.length = 13:
9228 One column is one chunk.
9229 popFront and popBack pop the left-most
9230 and right-most column, respectively.
9233 auto div = _source
.length
/ _chunkCount
;
9234 auto mod
= _source
.length
% _chunkCount
;
9237 : mod
* (div+1) + (i
-mod
) * div
9239 //auto len = i < mod
9248 EvenChunks
!Source
evenChunks(Source
)(Source source
, size_t chunkCount
)
9249 if (isForwardRange
!Source
&& hasLength
!Source
)
9251 return typeof(return)(source
, chunkCount
);
9257 import std
.algorithm
.comparison
: equal
;
9258 auto source
= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
9259 auto chunks
= evenChunks(source
, 3);
9260 assert(chunks
[0] == [1, 2, 3, 4]);
9261 assert(chunks
[1] == [5, 6, 7]);
9262 assert(chunks
[2] == [8, 9, 10]);
9267 import std
.algorithm
.comparison
: equal
;
9269 auto source
= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
9270 auto chunks
= evenChunks(source
, 3);
9271 assert(chunks
.back
== chunks
[2]);
9272 assert(chunks
.front
== chunks
[0]);
9273 assert(chunks
.length
== 3);
9274 assert(equal(retro(array(chunks
)), array(retro(chunks
))));
9276 auto chunks2
= chunks
.save
;
9278 assert(chunks
[0] == [5, 6, 7]);
9279 assert(chunks
[1] == [8, 9, 10]);
9281 assert(chunks2
[1] == [5, 6, 7]);
9282 assert(chunks2
.length
== 2);
9284 static assert(isRandomAccessRange
!(typeof(chunks
)));
9289 import std
.algorithm
.comparison
: equal
;
9292 auto chunks
= source
.evenChunks(0);
9293 assert(chunks
.length
== 0);
9294 chunks
= source
.evenChunks(3);
9295 assert(equal(chunks
, [[], [], []]));
9296 chunks
= [1, 2, 3].evenChunks(5);
9297 assert(equal(chunks
, [[1], [2], [3], [], []]));
9301 A fixed-sized sliding window iteration
9302 of size `windowSize` over a `source` range by a custom `stepSize`.
9304 The `Source` range must be at least a $(REF_ALTTEXT ForwardRange, isForwardRange, std,range,primitives)
9305 and the `windowSize` must be greater than zero.
9307 For `windowSize = 1` it splits the range into single element groups (aka `unflatten`)
9308 For `windowSize = 2` it is similar to `zip(source, source.save.dropOne)`.
9311 f = Whether the last element has fewer elements than `windowSize`
9312 it should be be ignored (`No.withPartial`) or added (`Yes.withPartial`)
9313 source = Range from which the slide will be selected
9314 windowSize = Sliding window size
9315 stepSize = Steps between the windows (by default 1)
9317 Returns: Range of all sliding windows with propagated bi-directionality,
9318 forwarding, random access, and slicing.
9320 Note: To avoid performance overhead, $(REF_ALTTEXT bi-directionality, isBidirectionalRange, std,range,primitives)
9321 is only available when $(REF hasSlicing, std,range,primitives)
9322 and $(REF hasLength, std,range,primitives) are true.
9324 See_Also: $(LREF chunks)
9326 auto slide(Flag
!"withPartial" f
= Yes
.withPartial
,
9327 Source
)(Source source
, size_t windowSize
, size_t stepSize
= 1)
9328 if (isForwardRange
!Source
)
9330 return Slides
!(f
, Source
)(source
, windowSize
, stepSize
);
9333 /// Iterate over ranges with windows
9334 @safe pure nothrow unittest
9336 import std
.algorithm
.comparison
: equal
;
9338 assert([0, 1, 2, 3].slide(2).equal
!equal(
9339 [[0, 1], [1, 2], [2, 3]]
9342 assert(5.iota
.slide(3).equal
!equal(
9343 [[0, 1, 2], [1, 2, 3], [2, 3, 4]]
9347 /// set a custom stepsize (default 1)
9348 @safe pure nothrow unittest
9350 import std
.algorithm
.comparison
: equal
;
9352 assert(6.iota
.slide(1, 2).equal
!equal(
9356 assert(6.iota
.slide(2, 4).equal
!equal(
9360 assert(iota(7).slide(2, 2).equal
!equal(
9361 [[0, 1], [2, 3], [4, 5], [6]]
9364 assert(iota(12).slide(2, 4).equal
!equal(
9365 [[0, 1], [4, 5], [8, 9]]
9369 /// Allow the last slide to have fewer elements than windowSize
9370 @safe pure nothrow unittest
9372 import std
.algorithm
.comparison
: equal
;
9374 assert(3.iota
.slide
!(No
.withPartial
)(4).empty
);
9375 assert(3.iota
.slide
!(Yes
.withPartial
)(4).equal
!equal(
9380 /// Count all the possible substrings of length 2
9381 @safe pure nothrow unittest
9383 import std
.algorithm
.iteration
: each
;
9386 "AGAGA"d
.slide
!(Yes
.withPartial
)(2).each
!(a
=> d
[a
]++);
9387 assert(d
== ["AG"d
: 2, "GA"d
: 2]);
9390 /// withPartial only has an effect if last element in the range doesn't have the full size
9391 @safe pure nothrow unittest
9393 import std
.algorithm
.comparison
: equal
;
9395 assert(5.iota
.slide
!(Yes
.withPartial
)(3, 4).equal
!equal([[0, 1, 2], [4]]));
9396 assert(6.iota
.slide
!(Yes
.withPartial
)(3, 4).equal
!equal([[0, 1, 2], [4, 5]]));
9397 assert(7.iota
.slide
!(Yes
.withPartial
)(3, 4).equal
!equal([[0, 1, 2], [4, 5, 6]]));
9399 assert(5.iota
.slide
!(No
.withPartial
)(3, 4).equal
!equal([[0, 1, 2]]));
9400 assert(6.iota
.slide
!(No
.withPartial
)(3, 4).equal
!equal([[0, 1, 2]]));
9401 assert(7.iota
.slide
!(No
.withPartial
)(3, 4).equal
!equal([[0, 1, 2], [4, 5, 6]]));
9404 private struct Slides(Flag
!"withPartial" withPartial
= Yes
.withPartial
, Source
)
9405 if (isForwardRange
!Source
)
9412 static if (hasLength
!Source
)
9414 enum needsEndTracker
= false;
9418 // If there's no information about the length, track needs to be kept manually
9420 enum needsEndTracker
= true;
9425 static if (hasSlicing
!Source
)
9426 enum hasSliceToEnd
= hasSlicing
!Source
&& is(typeof(Source
.init
[0 .. $]) == Source
);
9428 static if (withPartial
)
9429 bool hasShownPartialBefore
;
9432 /// Standard constructor
9433 this(Source source
, size_t windowSize
, size_t stepSize
)
9435 assert(windowSize
> 0, "windowSize must be greater than zero");
9436 assert(stepSize
> 0, "stepSize must be greater than zero");
9437 this.source
= source
;
9438 this.windowSize
= windowSize
;
9439 this.stepSize
= stepSize
;
9441 static if (needsEndTracker
)
9443 // `nextSource` is used to "look one step into the future" and check for the end
9444 // this means `nextSource` is advanced by `stepSize` on every `popFront`
9445 nextSource
= source
.save
;
9446 auto poppedElems
= nextSource
.popFrontN(windowSize
);
9455 static if (withPartial
)
9457 static if (needsEndTracker
)
9459 if (nextSource
.empty
)
9460 hasShownPartialBefore
= true;
9464 if (source
.length
<= windowSize
)
9465 hasShownPartialBefore
= true;
9470 // empty source range is needed, s.t. length, slicing etc. works properly
9471 static if (needsEndTracker
)
9473 if (poppedElems
< windowSize
)
9478 if (source
.length
< windowSize
)
9484 /// Forward range primitives. Always present.
9485 @property auto front()
9487 assert(!empty
, "Attempting to access front on an empty slide.");
9488 static if (hasSlicing
!Source
&& hasLength
!Source
)
9490 static if (withPartial
)
9492 import std
.algorithm
.comparison
: min
;
9493 return source
[0 .. min(windowSize
, source
.length
)];
9497 assert(windowSize
<= source
.length
, "The last element is smaller than the current windowSize.");
9498 return source
[0 .. windowSize
];
9503 static if (withPartial
)
9504 return source
.save
.take(windowSize
);
9506 return source
.save
.takeExactly(windowSize
);
9513 assert(!empty
, "Attempting to call popFront() on an empty slide.");
9514 source
.popFrontN(stepSize
);
9522 static if (withPartial
)
9524 if (hasShownPartialBefore
)
9528 static if (needsEndTracker
)
9530 // Check the upcoming slide
9531 auto poppedElements
= nextSource
.popFrontN(stepSize
);
9532 static if (withPartial
)
9534 if (poppedElements
< stepSize || nextSource
.empty
)
9535 hasShownPartialBefore
= true;
9539 if (poppedElements
< stepSize
)
9545 static if (withPartial
)
9547 if (source
.length
<= windowSize
)
9548 hasShownPartialBefore
= true;
9552 if (source
.length
< windowSize
)
9558 static if (!isInfinite
!Source
)
9561 @property bool empty() const
9573 @property typeof(this) save()
9575 return typeof(this)(source
.save
, windowSize
, stepSize
);
9578 static if (hasLength
!Source
)
9580 // gaps between the last element and the end of the range
9581 private size_t
gap()
9585 * - In the following `end` is the exclusive end as used in opSlice
9586 * - For the trivial case with `stepSize = 1` `end` is at `len`:
9588 * iota(4).slide(2) = [[0, 1], [1, 2], [2, 3]] (end = 4)
9589 * iota(4).slide(3) = [[0, 1, 2], [1, 2, 3]] (end = 4)
9591 * - For the non-trivial cases, we need to calculate the gap
9592 * between `len` and `end` - this is the number of missing elements
9593 * from the input range:
9595 * iota(7).slide(2, 3) = [[0, 1], [3, 4]] || <gap: 2> 6
9596 * iota(7).slide(2, 4) = [[0, 1], [4, 5]] || <gap: 1> 6
9597 * iota(7).slide(1, 5) = [[0], [5]] || <gap: 1> 6
9599 * As it can be seen `gap` can be at most `stepSize - 1`
9600 * More generally the elements of the sliding window with
9601 * `w = windowSize` and `s = stepSize` are:
9603 * [0, w], [s, s + w], [2 * s, 2 * s + w], ... [n * s, n * s + w]
9605 * We can thus calculate the gap between the `end` and `len` as:
9607 * gap = len - (n * s + w) = len - w - (n * s)
9609 * As we aren't interested in exact value of `n`, but the best
9610 * minimal `gap` value, we can use modulo to "cut" `len - w` optimally:
9612 * gap = len - w - (s - s ... - s) = (len - w) % s
9616 * iota(7).slide(2, 3) = [[0, 1], [3, 4]]
9617 * gap: (7 - 2) % 3 = 5 % 3 = 2
9620 * iota(7).slide(4, 2) = [[0, 1, 2, 3], [2, 3, 4, 5]]
9621 * gap: (7 - 4) % 2 = 3 % 2 = 1
9624 return (source
.length
- windowSize
) % stepSize
;
9627 private size_t
numberOfFullFrames()
9630 5.iota.slides(2, 1) => [0, 1], [1, 2], [2, 3], [3, 4] (4)
9631 7.iota.slides(2, 2) => [0, 1], [2, 3], [4, 5], [6] (3)
9632 7.iota.slides(2, 3) => [0, 1], [3, 4], [6] (2)
9633 6.iota.slides(3, 2) => [0, 1, 2], [2, 3, 4], [4, 5] (2)
9634 7.iota.slides(3, 3) => [0, 1, 2], [3, 4, 5], [6] (2)
9636 As the last window is only added iff its complete,
9637 we don't count the last window except if it's full due to integer rounding.
9639 return 1 + (source
.length
- windowSize
) / stepSize
;
9642 // Whether the last slide frame size is less than windowSize
9643 private bool hasPartialElements()
9645 static if (withPartial
)
9646 return gap
!= 0 && source
.length
> numberOfFullFrames
* stepSize
;
9651 /// Length. Only if `hasLength!Source` is `true`
9652 @property size_t
length()
9654 if (source
.length
< windowSize
)
9656 static if (withPartial
)
9657 return source
.length
> 0;
9664 We bump the pointer by stepSize for every element.
9665 If withPartial, we don't count the last element if its size
9669 [p, p + stepSize, ..., p + stepSize * n]
9671 5.iota.slides(2, 1) => [0, 1], [1, 2], [2, 3], [3, 4] (4)
9672 7.iota.slides(2, 2) => [0, 1], [2, 3], [4, 5], [6] (4)
9673 7.iota.slides(2, 3) => [0, 1], [3, 4], [6] (3)
9674 7.iota.slides(3, 2) => [0, 1, 2], [2, 3, 4], [4, 5, 6] (3)
9675 7.iota.slides(3, 3) => [0, 1, 2], [3, 4, 5], [6] (3)
9677 return numberOfFullFrames
+ hasPartialElements
;
9682 static if (hasSlicing
!Source
)
9685 Indexing and slicing operations. Provided only if
9686 `hasSlicing!Source` is `true`.
9688 auto opIndex(size_t index
)
9690 immutable start
= index
* stepSize
;
9692 static if (isInfinite
!Source
)
9694 immutable end
= start
+ windowSize
;
9698 import std
.algorithm
.comparison
: min
;
9700 immutable len
= source
.length
;
9701 assert(start
< len
, "slide index out of bounds");
9702 immutable end
= min(start
+ windowSize
, len
);
9705 return source
[start
.. end
];
9708 static if (!isInfinite
!Source
)
9711 typeof(this) opSlice(size_t lower
, size_t upper
)
9713 import std
.algorithm
.comparison
: min
;
9715 assert(upper
<= length
, "slide slicing index out of bounds");
9716 assert(lower
<= upper
, "slide slicing index out of bounds");
9721 immutable len
= source
.length
;
9723 static if (withPartial
)
9725 import std
.algorithm
.comparison
: max
;
9728 return this[$ .. $];
9731 A) If `stepSize` >= `windowSize` => `rightPos = upper`
9733 [0, 1, 2, 3, 4, 5, 6].slide(2, 3) -> s = [[0, 1], [3, 4], [6]]
9734 rightPos for s[0 .. 2]: (upper=2) * (stepSize=3) = 6
9735 6.iota.slide(2, 3) = [[0, 1], [3, 4]]
9737 B) If `stepSize` < `windowSize` => add `windowSize - stepSize` to `upper`
9739 [0, 1, 2, 3].slide(2) = [[0, 1], [1, 2], [2, 3]]
9740 rightPos for s[0 .. 1]: = (upper=1) * (stepSize=1) = 1
9741 1.iota.slide(2) = [[0]]
9743 rightPos for s[0 .. 1]: = (upper=1) * (stepSize=1) + (windowSize-stepSize=1) = 2
9744 1.iota.slide(2) = [[0, 1]]
9748 20.iota.slide(7, 6)[0 .. 2]
9749 rightPos: (upper=2) * (stepSize=6) = 12.iota
9750 12.iota.slide(7, 6) = [[0, 1, 2, 3, 4, 5, 6], [6, 7, 8, 9, 10, 11]]
9752 Now we add up for the difference between `windowSize` and `stepSize`:
9754 rightPos: (upper=2) * (stepSize=6) + (windowSize-stepSize=1) = 13.iota
9755 13.iota.slide(7, 6) = [[0, 1, 2, 3, 4, 5, 6], [6, 7, 8, 9, 10, 11, 12]]
9757 immutable rightPos
= min(len
, upper
+ max(0, windowSize
- stepSize
));
9762 After we have normalized `lower` and `upper` by `stepSize`,
9763 we only need to look at the case of `stepSize=1`.
9764 As `leftPos`, is equal to `lower`, we will only look `rightPos`.
9765 Notice that starting from `upper`,
9766 we only need to move for `windowSize - 1` to the right:
9768 - [0, 1, 2, 3].slide(2) -> s = [[0, 1], [1, 2], [2, 3]]
9769 rightPos for s[0 .. 3]: (upper=3) + (windowSize=2) - 1 = 4
9771 - [0, 1, 2, 3].slide(3) -> s = [[0, 1, 2], [1, 2, 3]]
9772 rightPos for s[0 .. 2]: (upper=2) + (windowSize=3) - 1 = 4
9774 - [0, 1, 2, 3, 4].slide(4) -> s = [[0, 1, 2, 3], [1, 2, 3, 4]]
9775 rightPos for s[0 .. 2]: (upper=2) + (windowSize=4) - 1 = 5
9777 immutable rightPos
= min(upper
+ windowSize
- 1, len
);
9780 return typeof(this)(source
[min(lower
, len
) .. rightPos
], windowSize
, stepSize
);
9783 else static if (hasSliceToEnd
)
9785 // For slicing an infinite chunk, we need to slice the source to the infinite end.
9786 auto opSlice(size_t lower
, size_t upper
)
9788 assert(lower
<= upper
, "slide slicing index out of bounds");
9789 return typeof(this)(source
[lower
* stepSize
.. $], windowSize
, stepSize
)
9790 .takeExactly(upper
- lower
);
9794 static if (isInfinite
!Source
)
9796 static if (hasSliceToEnd
)
9798 private static struct DollarToken
{}
9799 DollarToken
opDollar()
9801 return DollarToken();
9804 typeof(this) opSlice(size_t lower
, DollarToken
)
9806 return typeof(this)(source
[lower
* stepSize
.. $], windowSize
, stepSize
);
9812 // Dollar token carries a static type, with no extra information.
9813 // It can lazily transform into source.length on algorithmic
9814 // operations such as : slide[$/2, $-1];
9815 private static struct DollarToken
9817 private size_t _length
;
9821 DollarToken
opDollar()
9823 return DollarToken(this.length
);
9826 // Optimized slice overloads optimized for using dollar.
9827 typeof(this) opSlice(DollarToken
, DollarToken
)
9829 static if (hasSliceToEnd
)
9831 return typeof(this)(source
[$ .. $], windowSize
, stepSize
);
9835 immutable len
= source
.length
;
9836 return typeof(this)(source
[len
.. len
], windowSize
, stepSize
);
9840 // Optimized slice overloads optimized for using dollar.
9841 typeof(this) opSlice(size_t lower
, DollarToken
)
9843 import std
.algorithm
.comparison
: min
;
9844 assert(lower
<= length
, "slide slicing index out of bounds");
9846 static if (hasSliceToEnd
)
9848 return typeof(this)(source
[min(lower
, source
.length
) .. $], windowSize
, stepSize
);
9852 immutable len
= source
.length
;
9853 return typeof(this)(source
[min(lower
, len
) .. len
], windowSize
, stepSize
);
9857 // Optimized slice overloads optimized for using dollar.
9858 typeof(this) opSlice(DollarToken
, size_t upper
)
9860 assert(upper
== length
, "slide slicing index out of bounds");
9861 return this[$ .. $];
9865 // Bidirectional range primitives
9866 static if (!isInfinite
!Source
)
9869 Bidirectional range primitives. Provided only if both
9870 `hasSlicing!Source` and `!isInfinite!Source` are `true`.
9872 @property auto back()
9874 import std
.algorithm
.comparison
: max
;
9876 assert(!empty
, "Attempting to access front on an empty slide");
9878 immutable len
= source
.length
;
9880 static if (withPartial
)
9882 if (source
.length
<= windowSize
)
9883 return source
[0 .. source
.length
];
9885 if (hasPartialElements
)
9886 return source
[numberOfFullFrames
* stepSize
.. len
];
9889 // check for underflow
9890 immutable start
= (len
> windowSize
+ gap
) ? len
- windowSize
- gap
: 0;
9891 return source
[start
.. len
- gap
];
9897 assert(!empty
, "Attempting to call popBack() on an empty slide");
9900 immutable end
= source
.length
> stepSize ? source
.length
- stepSize
: 0;
9902 static if (withPartial
)
9904 if (hasShownPartialBefore || source
.empty
)
9910 // pop by stepSize, except for the partial frame at the end
9911 if (hasPartialElements
)
9912 source
= source
[0 .. source
.length
- gap
];
9914 source
= source
[0 .. end
];
9918 source
= source
[0 .. end
];
9921 if (source
.length
< windowSize
)
9929 @safe pure nothrow @nogc unittest
9931 import std
.algorithm
.comparison
: equal
;
9933 static immutable res1
= [[0], [1], [2], [3]];
9934 assert(4.iota
.slide
!(Yes
.withPartial
)(1).equal
!equal(res1
));
9936 static immutable res2
= [[0, 1], [1, 2], [2, 3]];
9937 assert(4.iota
.slide
!(Yes
.withPartial
)(2).equal
!equal(res2
));
9940 // test different window sizes
9941 @safe pure nothrow unittest
9943 import std
.array
: array
;
9944 import std
.algorithm
.comparison
: equal
;
9946 assert([0, 1, 2, 3].slide
!(Yes
.withPartial
)(1).array
== [[0], [1], [2], [3]]);
9947 assert([0, 1, 2, 3].slide
!(Yes
.withPartial
)(2).array
== [[0, 1], [1, 2], [2, 3]]);
9948 assert([0, 1, 2, 3].slide
!(Yes
.withPartial
)(3).array
== [[0, 1, 2], [1, 2, 3]]);
9949 assert([0, 1, 2, 3].slide
!(Yes
.withPartial
)(4).array
== [[0, 1, 2, 3]]);
9950 assert([0, 1, 2, 3].slide
!(No
.withPartial
)(5).walkLength
== 0);
9951 assert([0, 1, 2, 3].slide
!(Yes
.withPartial
)(5).array
== [[0, 1, 2, 3]]);
9953 assert(iota(2).slide
!(Yes
.withPartial
)(2).front
.equal([0, 1]));
9954 assert(iota(3).slide
!(Yes
.withPartial
)(2).equal
!equal([[0, 1],[1, 2]]));
9955 assert(iota(3).slide
!(Yes
.withPartial
)(3).equal
!equal([[0, 1, 2]]));
9956 assert(iota(3).slide
!(No
.withPartial
)(4).walkLength
== 0);
9957 assert(iota(3).slide
!(Yes
.withPartial
)(4).equal
!equal([[0, 1, 2]]));
9958 assert(iota(1, 4).slide
!(Yes
.withPartial
)(1).equal
!equal([[1], [2], [3]]));
9959 assert(iota(1, 4).slide
!(Yes
.withPartial
)(3).equal
!equal([[1, 2, 3]]));
9962 // test combinations
9963 @safe pure nothrow unittest
9965 import std
.algorithm
.comparison
: equal
;
9966 import std
.typecons
: tuple
;
9970 t(t(1, 1), [[0], [1], [2], [3], [4], [5]]),
9971 t(t(1, 2), [[0], [2], [4]]),
9972 t(t(1, 3), [[0], [3]]),
9973 t(t(1, 4), [[0], [4]]),
9974 t(t(1, 5), [[0], [5]]),
9975 t(t(2, 1), [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5]]),
9976 t(t(2, 2), [[0, 1], [2, 3], [4, 5]]),
9977 t(t(2, 3), [[0, 1], [3, 4]]),
9978 t(t(2, 4), [[0, 1], [4, 5]]),
9979 t(t(3, 1), [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]]),
9980 t(t(3, 3), [[0, 1, 2], [3, 4, 5]]),
9981 t(t(4, 1), [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]),
9982 t(t(4, 2), [[0, 1, 2, 3], [2, 3, 4, 5]]),
9983 t(t(5, 1), [[0, 1, 2, 3, 4], [1, 2, 3, 4, 5]]),
9986 static foreach (Partial
; [Yes
.withPartial
, No
.withPartial
])
9988 assert(6.iota
.slide
!Partial(e
[0].expand
).equal
!equal(e
[1]));
9990 auto listSpecial
= [
9991 t(t(2, 5), [[0, 1], [5]]),
9992 t(t(3, 2), [[0, 1, 2], [2, 3, 4], [4, 5]]),
9993 t(t(3, 4), [[0, 1, 2], [4, 5]]),
9994 t(t(4, 3), [[0, 1, 2, 3], [3, 4, 5]]),
9995 t(t(5, 2), [[0, 1, 2, 3, 4], [2, 3, 4, 5]]),
9996 t(t(5, 3), [[0, 1, 2, 3, 4], [3, 4, 5]]),
9998 foreach (e
; listSpecial
)
10000 assert(6.iota
.slide
!(Yes
.withPartial
)(e
[0].expand
).equal
!equal(e
[1]));
10001 assert(6.iota
.slide
!(No
.withPartial
)(e
[0].expand
).equal
!equal(e
[1].dropBackOne
));
10005 // test emptiness and copyability
10006 @safe pure nothrow unittest
10008 import std
.algorithm
.comparison
: equal
;
10009 import std
.algorithm
.iteration
: map
;
10011 // check with empty input
10013 assert(d
.slide
!(Yes
.withPartial
)(2).empty
);
10014 assert(d
.slide
!(Yes
.withPartial
)(2, 2).empty
);
10017 auto e
= iota(5).slide
!(Yes
.withPartial
)(2);
10019 assert(e
.save
.equal
!equal([[1, 2], [2, 3], [3, 4]]));
10020 assert(e
.save
.equal
!equal([[1, 2], [2, 3], [3, 4]]));
10021 assert(e
.map
!"a.array".array
== [[1, 2], [2, 3], [3, 4]]);
10024 // test with strings
10025 @safe pure nothrow unittest
10027 import std
.algorithm
.iteration
: each
;
10030 "AGAGA"d
.slide
!(Yes
.withPartial
)(3).each
!(a
=> f
[a
]++);
10031 assert(f
== ["AGA"d
: 2, "GAG"d
: 1]);
10034 "ABCDEFG"d
.slide
!(Yes
.withPartial
)(3, 3).each
!(a
=> g
[a
]++);
10035 assert(g
== ["ABC"d
:1, "DEF"d
:1, "G": 1]);
10037 "ABCDEFG"d
.slide
!(No
.withPartial
)(3, 3).each
!(a
=> g
[a
]++);
10038 assert(g
== ["ABC"d
:1, "DEF"d
:1]);
10041 // test with utf8 strings
10045 import std
.algorithm
.comparison
: equal
;
10047 assert("ä.ö.ü.".slide
!(Yes
.withPartial
)(3, 2).equal
!equal(["ä.ö", "ö.ü", "ü."]));
10048 assert("ä.ö.ü.".slide
!(No
.withPartial
)(3, 2).equal
!equal(["ä.ö", "ö.ü"]));
10050 "😄😅😆😇😈😄😅😆😇😈".slide
!(Yes
.withPartial
)(2, 4).equal
!equal(["😄😅", "😈😄", "😇😈"]);
10051 "😄😅😆😇😈😄😅😆😇😈".slide
!(No
.withPartial
)(2, 4).equal
!equal(["😄😅", "😈😄", "😇😈"]);
10052 "😄😅😆😇😈😄😅😆😇😈".slide
!(Yes
.withPartial
)(3, 3).equal
!equal(["😄😅😆", "😇😈😄", "😅😆😇", "😈"]);
10053 "😄😅😆😇😈😄😅😆😇😈".slide
!(No
.withPartial
)(3, 3).equal
!equal(["😄😅😆", "😇😈😄", "😅😆😇"]);
10057 @safe pure nothrow unittest
10059 // Slides with fewer elements are empty or 1 for Yes.withPartial
10060 static foreach (expectedLength
, Partial
; [No
.withPartial
, Yes
.withPartial
])
10062 assert(3.iota
.slide
!(Partial
)(4, 2).walkLength
== expectedLength
);
10063 assert(3.iota
.slide
!(Partial
)(4).walkLength
== expectedLength
);
10064 assert(3.iota
.slide
!(Partial
)(4, 3).walkLength
== expectedLength
);
10067 static immutable list
= [
10068 // iota slide expected
10084 assert(e
[0].iota
.slide
!(Yes
.withPartial
)(e
[1], e
[2]).length
== e
[3]);
10085 assert(e
[0].iota
.slide
!(No
.withPartial
)(e
[1], e
[2]).length
== e
[4]);
10089 // test index and slicing
10090 @safe pure nothrow unittest
10092 import std
.algorithm
.comparison
: equal
;
10093 import std
.array
: array
;
10095 static foreach (Partial
; [Yes
.withPartial
, No
.withPartial
])
10097 foreach (s
; [5, 7, 10, 15, 20])
10098 foreach (windowSize
; 1 .. 10)
10099 foreach (stepSize
; 1 .. 10)
10101 auto r
= s
.iota
.slide
!Partial(windowSize
, stepSize
);
10102 auto arr
= r
.array
;
10103 assert(r
.length
== arr
.length
);
10106 foreach (i
; 0 .. arr
.length
)
10107 assert(r
[i
] == arr
[i
]);
10110 foreach (i
; 0 .. arr
.length
)
10112 foreach (j
; i
.. arr
.length
)
10113 assert(r
[i
.. j
].equal(arr
[i
.. j
]));
10115 assert(r
[i
.. $].equal(arr
[i
.. $]));
10118 // test opDollar slicing
10119 assert(r
[$/2 .. $].equal(arr
[$/2 .. $]));
10120 assert(r
[$ .. $].empty
);
10123 assert(r
[$ .. 0].empty
);
10124 assert(r
[$/2 .. $].empty
);
10131 // test with infinite ranges
10132 @safe pure nothrow unittest
10134 import std
.algorithm
.comparison
: equal
;
10136 static foreach (Partial
; [Yes
.withPartial
, No
.withPartial
])
10138 // InfiniteRange without RandomAccess
10139 auto fibs
= recurrence
!"a[n-1] + a[n-2]"(1, 1);
10140 assert(fibs
.slide
!Partial(2).take(2).equal
!equal([[1, 1], [1, 2]]));
10141 assert(fibs
.slide
!Partial(2, 3).take(2).equal
!equal([[1, 1], [3, 5]]));
10143 // InfiniteRange with RandomAccess and slicing
10144 auto odds
= sequence
!("a[0] + n * a[1]")(1, 2);
10145 auto oddsByPairs
= odds
.slide
!Partial(2);
10146 assert(oddsByPairs
.take(2).equal
!equal([[ 1, 3], [ 3, 5]]));
10147 assert(oddsByPairs
[1].equal([3, 5]));
10148 assert(oddsByPairs
[4].equal([9, 11]));
10150 static assert(hasSlicing
!(typeof(odds
)));
10151 assert(oddsByPairs
[3 .. 5].equal
!equal([[7, 9], [9, 11]]));
10152 assert(oddsByPairs
[3 .. $].take(2).equal
!equal([[7, 9], [9, 11]]));
10154 auto oddsWithGaps
= odds
.slide
!Partial(2, 4);
10155 assert(oddsWithGaps
.take(3).equal
!equal([[1, 3], [9, 11], [17, 19]]));
10156 assert(oddsWithGaps
[2].equal([17, 19]));
10157 assert(oddsWithGaps
[1 .. 3].equal
!equal([[9, 11], [17, 19]]));
10158 assert(oddsWithGaps
[1 .. $].take(2).equal
!equal([[9, 11], [17, 19]]));
10163 @safe pure nothrow unittest
10165 import std
.algorithm
.comparison
: equal
;
10167 static foreach (Partial
; [Yes
.withPartial
, No
.withPartial
])
10169 foreach (windowSize
; 1 .. 15)
10170 foreach (stepSize
; 1 .. 15)
10172 auto r
= 20.iota
.slide
!Partial(windowSize
, stepSize
);
10173 auto rArr
= r
.array
.retro
;
10174 auto rRetro
= r
.retro
;
10176 assert(rRetro
.length
== rArr
.length
);
10177 assert(rRetro
.equal(rArr
));
10178 assert(rRetro
.array
.retro
.equal(r
));
10183 // test with dummy ranges
10184 @safe pure nothrow unittest
10186 import std
.algorithm
.comparison
: equal
;
10187 import std
.internal
.test.dummyrange
: AllDummyRanges
;
10188 import std
.meta
: Filter
;
10190 static foreach (Range
; Filter
!(isForwardRange
, AllDummyRanges
))
10194 static foreach (Partial
; [Yes
.withPartial
, No
.withPartial
])
10196 assert(r
.slide
!Partial(1).equal
!equal(
10197 [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]]
10199 assert(r
.slide
!Partial(2).equal
!equal(
10200 [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10]]
10202 assert(r
.slide
!Partial(3).equal
!equal(
10203 [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6],
10204 [5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10]]
10206 assert(r
.slide
!Partial(6).equal
!equal(
10207 [[1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8],
10208 [4, 5, 6, 7, 8, 9], [5, 6, 7, 8, 9, 10]]
10213 assert(r
.slide
!(Yes
.withPartial
)(15).equal
!equal(iota(1, 11).only
));
10214 assert(r
.slide
!(Yes
.withPartial
)(15).walkLength
== 1);
10215 assert(r
.slide
!(No
.withPartial
)(15).empty
);
10216 assert(r
.slide
!(No
.withPartial
)(15).walkLength
== 0);
10220 // test with dummy ranges
10221 @safe pure nothrow unittest
10223 import std
.algorithm
.comparison
: equal
;
10224 import std
.internal
.test.dummyrange
: AllDummyRanges
;
10225 import std
.meta
: Filter
;
10226 import std
.typecons
: tuple
;
10229 static immutable list
= [
10230 // iota slide expected
10231 t(6, t(4, 2), [[1, 2, 3, 4], [3, 4, 5, 6]]),
10232 t(6, t(4, 6), [[1, 2, 3, 4]]),
10233 t(6, t(4, 1), [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6]]),
10234 t(7, t(4, 1), [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7]]),
10235 t(7, t(4, 3), [[1, 2, 3, 4], [4, 5, 6, 7]]),
10236 t(8, t(4, 2), [[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7, 8]]),
10237 t(8, t(4, 1), [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7], [5, 6, 7, 8]]),
10238 t(8, t(3, 4), [[1, 2, 3], [5, 6, 7]]),
10239 t(10, t(3, 7), [[1, 2, 3], [8, 9, 10]]),
10242 static foreach (Range
; Filter
!(isForwardRange
, AllDummyRanges
))
10243 static foreach (Partial
; [Yes
.withPartial
, No
.withPartial
])
10245 assert(Range().take(e
[0]).slide
!Partial(e
[1].expand
).equal
!equal(e
[2]));
10247 static immutable listSpecial
= [
10248 // iota slide expected
10249 t(6, t(4, 3), [[1, 2, 3, 4], [4, 5, 6]]),
10250 t(7, t(4, 5), [[1, 2, 3, 4], [6, 7]]),
10251 t(7, t(4, 4), [[1, 2, 3, 4], [5, 6, 7]]),
10252 t(7, t(4, 2), [[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7]]),
10253 t(8, t(4, 3), [[1, 2, 3, 4], [4, 5, 6, 7], [7, 8]]),
10254 t(8, t(3, 3), [[1, 2, 3], [4, 5, 6], [7, 8]]),
10255 t(8, t(3, 6), [[1, 2, 3], [7, 8]]),
10256 t(10, t(7, 6), [[1, 2, 3, 4, 5, 6, 7], [7, 8, 9, 10]]),
10257 t(10, t(3, 8), [[1, 2, 3], [9, 10]]),
10259 static foreach (Range
; Filter
!(isForwardRange
, AllDummyRanges
))
10260 static foreach (Partial
; [Yes
.withPartial
, No
.withPartial
])
10261 foreach (e
; listSpecial
)
10264 assert(r
.take(e
[0]).slide
!(Yes
.withPartial
)(e
[1].expand
).equal
!equal(e
[2]));
10265 assert(r
.take(e
[0]).slide
!(No
.withPartial
)(e
[1].expand
).equal
!equal(e
[2].dropBackOne
));
10269 // test reverse with dummy ranges
10270 @safe pure nothrow unittest
10272 import std
.algorithm
.comparison
: equal
;
10273 import std
.internal
.test.dummyrange
: AllDummyRanges
;
10274 import std
.meta
: Filter
, templateAnd
;
10275 import std
.typecons
: tuple
;
10278 static immutable list
= [
10280 t(1, 1, [[10], [9], [8], [7], [6], [5], [4], [3], [2], [1]]),
10281 t(2, 1, [[9, 10], [8, 9], [7, 8], [6, 7], [5, 6], [4, 5], [3, 4], [2, 3], [1, 2]]),
10282 t(5, 1, [[6, 7, 8, 9, 10], [5, 6, 7, 8, 9], [4, 5, 6, 7, 8],
10283 [3, 4, 5, 6, 7], [2, 3, 4, 5, 6], [1, 2, 3, 4, 5]]),
10284 t(2, 2, [[9, 10], [7, 8], [5, 6], [3, 4], [1, 2]]),
10285 t(2, 4, [[9, 10], [5, 6], [1, 2]]),
10288 static foreach (Range
; Filter
!(templateAnd
!(hasSlicing
, hasLength
, isBidirectionalRange
), AllDummyRanges
))
10291 static foreach (Partial
; [Yes
.withPartial
, No
.withPartial
])
10294 assert(r
.slide
!Partial(e
[0], e
[1]).retro
.equal
!equal(e
[2]));
10297 foreach (windowSize
; 1 .. 10)
10298 foreach (stepSize
; 1 .. 10)
10300 auto slider
= r
.slide
!Partial(windowSize
, stepSize
);
10301 auto sliderRetro
= slider
.retro
.array
;
10302 assert(slider
.length
== sliderRetro
.length
);
10303 assert(sliderRetro
.retro
.equal
!equal(slider
));
10308 assert(r
.slide
!(No
.withPartial
)(15).retro
.walkLength
== 0);
10309 assert(r
.slide
!(Yes
.withPartial
)(15).retro
.equal
!equal(iota(1, 11).only
));
10313 // test different sliceable ranges
10314 @safe pure nothrow unittest
10316 import std
.algorithm
.comparison
: equal
;
10317 import std
.internal
.test.dummyrange
: AllDummyRanges
;
10318 import std
.meta
: AliasSeq
;
10320 struct SliceableRange(Range
, Flag
!"withOpDollar" withOpDollar
= No
.withOpDollar
,
10321 Flag
!"withInfiniteness" withInfiniteness
= No
.withInfiniteness
)
10323 Range arr
= 10.iota
.array
; // similar to DummyRange
10324 @property auto save() { return typeof(this)(arr
); }
10325 @property auto front() { return arr
[0]; }
10326 void popFront() { arr
.popFront(); }
10327 auto opSlice(size_t i
, size_t j
)
10329 // subslices can't be infinite
10330 return SliceableRange
!(Range
, withOpDollar
, No
.withInfiniteness
)(arr
[i
.. j
]);
10333 static if (withInfiniteness
)
10335 enum empty
= false;
10339 @property bool empty() { return arr
.empty
; }
10340 @property auto length() { return arr
.length
; }
10343 static if (withOpDollar
)
10345 static if (withInfiniteness
)
10348 Dollar
opDollar() const { return Dollar
.init
; }
10351 typeof(this) opSlice(size_t lower
, Dollar
)
10353 return typeof(this)(arr
[lower
.. $]);
10359 alias opDollar
= length
;
10364 import std
.meta
: Filter
, templateNot
;
10365 alias SliceableDummyRanges
= Filter
!(hasSlicing
, AllDummyRanges
);
10367 static foreach (Partial
; [Yes
.withPartial
, No
.withPartial
])
10369 static foreach (Range
; SliceableDummyRanges
)
10373 r
.arr
[] -= 1; // use a 0-based array (for clarity)
10375 assert(r
.slide
!Partial(2)[0].equal([0, 1]));
10376 assert(r
.slide
!Partial(2)[1].equal([1, 2]));
10379 auto s
= r
.slide
!Partial(2);
10380 assert(s
[0 .. 2].equal
!equal([[0, 1], [1, 2]]));
10382 assert(s
[0 .. 2].equal
!equal([[0, 1], [1, 2]]));
10384 assert(r
.slide
!Partial(3)[1 .. 3].equal
!equal([[1, 2, 3], [2, 3, 4]]));
10387 static foreach (Range
; Filter
!(templateNot
!isInfinite
, SliceableDummyRanges
))
10391 r
.arr
[] -= 1; // use a 0-based array (for clarity)
10393 assert(r
.slide
!(No
.withPartial
)(6).equal
!equal(
10394 [[0, 1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7],
10395 [3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9]]
10397 assert(r
.slide
!(No
.withPartial
)(16).empty
);
10399 assert(r
.slide
!Partial(4)[0 .. $].equal(r
.slide
!Partial(4)));
10400 assert(r
.slide
!Partial(2)[$/2 .. $].equal
!equal([[4, 5], [5, 6], [6, 7], [7, 8], [8, 9]]));
10401 assert(r
.slide
!Partial(2)[$ .. $].empty
);
10403 assert(r
.slide
!Partial(3).retro
.equal
!equal(
10404 [[7, 8, 9], [6, 7, 8], [5, 6, 7], [4, 5, 6], [3, 4, 5], [2, 3, 4], [1, 2, 3], [0, 1, 2]]
10410 // separate checks for infinity
10411 auto infIndex
= SliceableRange
!(T
, No
.withOpDollar
, Yes
.withInfiniteness
)([0, 1, 2, 3]);
10412 assert(infIndex
.slide
!Partial(2)[0].equal([0, 1]));
10413 assert(infIndex
.slide
!Partial(2)[1].equal([1, 2]));
10415 auto infDollar
= SliceableRange
!(T
, Yes
.withOpDollar
, Yes
.withInfiniteness
)();
10416 assert(infDollar
.slide
!Partial(2)[1 .. $].front
.equal([1, 2]));
10417 assert(infDollar
.slide
!Partial(4)[0 .. $].front
.equal([0, 1, 2, 3]));
10418 assert(infDollar
.slide
!Partial(4)[2 .. $].front
.equal([2, 3, 4, 5]));
10422 // https://issues.dlang.org/show_bug.cgi?id=19082
10425 import std
.algorithm
.comparison
: equal
;
10426 import std
.algorithm
.iteration
: map
;
10427 assert([1].map
!(x
=> x
).slide(2).equal
!equal([[1]]));
10430 // https://issues.dlang.org/show_bug.cgi?id=19642
10433 import std
.algorithm
.comparison
: equal
;
10434 import std
.algorithm
.iteration
: splitter
;
10436 assert("ab cd".splitter(' ').slide
!(No
.withPartial
)(2).equal
!equal([["ab", "cd"]]));
10439 // https://issues.dlang.org/show_bug.cgi?id=23976
10442 import std
.algorithm
.comparison
: equal
;
10443 import std
.algorithm
.iteration
: splitter
;
10445 assert("1<2".splitter('<').slide(2).equal
!equal([["1", "2"]]));
10448 private struct OnlyResult(Values
...)
10449 if (Values
.length
> 1)
10451 import std
.meta
: ApplyRight
;
10452 import std
.traits
: isAssignable
;
10454 private enum arity
= Values
.length
;
10456 private alias UnqualValues
= staticMap
!(Unqual
, Values
);
10458 private enum canAssignElements
= allSatisfy
!(
10459 ApplyRight
!(isAssignable
, CommonType
!Values
),
10463 private this(return scope ref Values values
)
10465 ref @trusted unqual(T
)(ref T x
){return cast() x
;}
10467 // TODO: this calls any possible copy constructors without qualifiers.
10468 // Find a way to initialize values using qualified copy constructors.
10469 static foreach (i
; 0 .. Values
.length
)
10471 this.values
[i
] = unqual(values
[i
]);
10473 this.backIndex
= arity
;
10476 bool empty() @property
10478 return frontIndex
>= backIndex
;
10481 CommonType
!Values
front() @property
10483 assert(!empty
, "Attempting to fetch the front of an empty Only range");
10487 static if (canAssignElements
)
10489 void front(CommonType
!Values value
) @property
10491 assert(!empty
, "Attempting to assign the front of an empty Only range");
10498 assert(!empty
, "Attempting to popFront an empty Only range");
10502 CommonType
!Values
back() @property
10504 assert(!empty
, "Attempting to fetch the back of an empty Only range");
10505 return this[$ - 1];
10508 static if (canAssignElements
)
10510 void back(CommonType
!Values value
) @property
10512 assert(!empty
, "Attempting to assign the back of an empty Only range");
10513 this[$ - 1] = value
;
10519 assert(!empty
, "Attempting to popBack an empty Only range");
10523 OnlyResult
save() @property
10528 size_t
length() const @property
10530 return backIndex
- frontIndex
;
10533 alias opDollar
= length
;
10535 @trusted CommonType
!Values
opIndex(size_t idx
)
10537 // when i + idx points to elements popped
10539 assert(idx
< length
, "Attempting to fetch an out of bounds index from an Only range");
10540 final switch (frontIndex
+ idx
)
10541 static foreach (i
, T
; Values
)
10543 return cast(T
) values
[i
];
10546 static if (canAssignElements
)
10548 void opIndexAssign(CommonType
!Values value
, size_t idx
)
10550 assert(idx
< length
, "Attempting to assign to an out of bounds index of an Only range");
10551 final switch (frontIndex
+ idx
)
10552 static foreach (i
; 0 .. Values
.length
)
10558 OnlyResult
opSlice()
10563 OnlyResult
opSlice(size_t from
, size_t to
)
10565 OnlyResult result
= this;
10566 result
.frontIndex
+= from
;
10567 result
.backIndex
= this.frontIndex
+ to
;
10570 "Attempting to slice an Only range with a larger first argument than the second."
10574 "Attempting to slice using an out of bounds index on an Only range"
10579 private size_t frontIndex
= 0;
10580 private size_t backIndex
= 0;
10582 // https://issues.dlang.org/show_bug.cgi?id=10643
10585 import std
.traits
: hasElaborateAssign
;
10586 static if (hasElaborateAssign
!T
)
10587 private UnqualValues values
;
10589 private UnqualValues values
= void;
10592 // These may alias to shared or immutable data. Do not let the user
10593 // to access these directly, and do not allow mutation without checking
10595 private UnqualValues values
;
10598 // Specialize for single-element results
10599 private struct OnlyResult(T
)
10601 import std
.traits
: isAssignable
;
10603 @property T
front()
10605 assert(!empty
, "Attempting to fetch the front of an empty Only range");
10606 return fetchFront();
10608 static if (isAssignable
!T
)
10610 @property void front(T value
)
10612 assert(!empty
, "Attempting to assign the front of an empty Only range");
10613 assignFront(value
);
10618 assert(!empty
, "Attempting to fetch the back of an empty Only range");
10619 return fetchFront();
10621 static if (isAssignable
!T
)
10623 @property void back(T value
)
10625 assert(!empty
, "Attempting to assign the front of an empty Only range");
10626 assignFront(value
);
10629 @property bool empty() const { return _empty
; }
10630 @property size_t
length() const { return !_empty
; }
10631 @property auto save() { return this; }
10634 assert(!_empty
, "Attempting to popFront an empty Only range");
10639 assert(!_empty
, "Attempting to popBack an empty Only range");
10642 alias opDollar
= length
;
10644 // FIXME Workaround for https://issues.dlang.org/show_bug.cgi?id=24415
10645 import std
.traits
: hasElaborateCopyConstructor
;
10646 static if (hasElaborateCopyConstructor
!T
)
10648 private static struct WorkaroundBugzilla24415
{}
10649 public this()(WorkaroundBugzilla24415
) {}
10652 private this()(return scope auto ref T value
)
10654 ref @trusted unqual(ref T x
){return cast() x
;}
10655 // TODO: this calls the possible copy constructor without qualifiers.
10656 // Find a way to initialize value using a qualified copy constructor.
10657 this._value
= unqual(value
);
10658 this._empty
= false;
10661 T
opIndex(size_t i
)
10663 assert(!_empty
&& i
== 0, "Attempting to fetch an out of bounds index from an Only range");
10664 return fetchFront();
10667 static if (isAssignable
!T
)
10669 void opIndexAssign(T value
, size_t i
)
10671 assert(!_empty
&& i
== 0, "Attempting to assign an out of bounds index of an Only range");
10672 assignFront(value
);
10676 OnlyResult
opSlice()
10681 OnlyResult
opSlice(size_t from
, size_t to
)
10685 "Attempting to slice an Only range with a larger first argument than the second."
10689 "Attempting to slice using an out of bounds index on an Only range"
10691 OnlyResult copy
= this;
10692 copy
._empty
= _empty || from
== to
;
10696 // This may alias to shared or immutable data. Do not let the user
10697 // to access this directly, and do not allow mutation without checking
10699 private Unqual
!T _value
;
10700 private bool _empty
= true;
10701 private @trusted T
fetchFront()
10703 return *cast(T
*)&_value
;
10705 static if (isAssignable
!T
)
10707 private @trusted void assignFront(T newValue
)
10709 *cast(T
*) &_value
= newValue
;
10715 Assemble `values` into a range that carries all its
10718 Useful when a single value or multiple disconnected values
10719 must be passed to an algorithm expecting a range, without
10720 having to perform dynamic memory allocation.
10722 As copying the range means copying all elements, it can be
10723 safely returned from functions. For the same reason, copying
10724 the returned range may be expensive for a large number of arguments.
10727 values = the values to assemble together
10730 A `RandomAccessRange` of the assembled values.
10732 The returned range can be sliced. Its elements can be assigned to if every
10733 type in `Values` supports assignment from the range's element type.
10735 See_Also: $(LREF chain) to chain ranges
10737 auto only(Values
...)(return scope Values values
)
10738 if (!is(CommonType
!Values
== void))
10740 return OnlyResult
!Values(values
);
10746 // cannot use noreturn due to https://issues.dlang.org/show_bug.cgi?id=22383
10747 struct EmptyElementType
{}
10748 EmptyElementType
[] result
;
10755 import std
.algorithm
.comparison
: equal
;
10756 import std
.algorithm
.iteration
: filter
, joiner
, map
;
10757 import std
.algorithm
.searching
: findSplitBefore
;
10758 import std
.uni
: isUpper
;
10760 assert(equal(only('♡'), "♡"));
10761 assert([1, 2, 3, 4].findSplitBefore(only(3))[0] == [1, 2]);
10763 assert(only("one", "two", "three").joiner(" ").equal("one two three"));
10765 string title
= "The D Programming Language";
10767 .filter
!isUpper
// take the upper case letters
10768 .map
!only
// make each letter its own range
10769 .joiner(".") // join the ranges together lazily
10770 .equal("T.D.P.L"));
10773 // https://issues.dlang.org/show_bug.cgi?id=20314
10776 import std
.algorithm
.iteration
: joiner
;
10778 const string s
= "foo", t
= "bar";
10780 assert([only(s
, t
), only(t
, s
)].joiner(only(", ")).join
== "foobar, barfoo");
10783 // Tests the zero-element result
10786 import std
.algorithm
.comparison
: equal
;
10788 auto emptyRange
= only();
10790 alias EmptyRange
= typeof(emptyRange
);
10791 static assert(isInputRange
!EmptyRange
);
10792 static assert(isForwardRange
!EmptyRange
);
10793 static assert(isBidirectionalRange
!EmptyRange
);
10794 static assert(isRandomAccessRange
!EmptyRange
);
10795 static assert(hasLength
!EmptyRange
);
10796 static assert(hasSlicing
!EmptyRange
);
10798 assert(emptyRange
.empty
);
10799 assert(emptyRange
.length
== 0);
10800 assert(emptyRange
.equal(emptyRange
[]));
10801 assert(emptyRange
.equal(emptyRange
.save
));
10802 assert(emptyRange
[0 .. 0].equal(emptyRange
));
10805 // Tests the single-element result
10808 import std
.algorithm
.comparison
: equal
;
10809 import std
.typecons
: tuple
;
10810 foreach (x
; tuple(1, '1', 1.0, "1", [1]))
10813 typeof(x
)[] e
= [];
10814 assert(a
.front
== x
);
10815 assert(a
.back
== x
);
10817 assert(a
.length
== 1);
10818 assert(equal(a
, a
[]));
10819 assert(equal(a
, a
[0 .. 1]));
10820 assert(equal(a
[0 .. 0], e
));
10821 assert(equal(a
[1 .. 1], e
));
10825 assert(equal(a
, b
));
10827 assert(a
.empty
&& a
.length
== 0 && a
[].empty
);
10829 assert(b
.empty
&& b
.length
== 0 && b
[].empty
);
10831 alias A
= typeof(a
);
10832 static assert(isInputRange
!A
);
10833 static assert(isForwardRange
!A
);
10834 static assert(isBidirectionalRange
!A
);
10835 static assert(isRandomAccessRange
!A
);
10836 static assert(hasLength
!A
);
10837 static assert(hasSlicing
!A
);
10840 auto imm
= only
!(immutable int)(1);
10841 immutable int[] imme
= [];
10842 assert(imm
.front
== 1);
10843 assert(imm
.back
== 1);
10844 assert(!imm
.empty
);
10845 assert(imm
.init
.empty
); // https://issues.dlang.org/show_bug.cgi?id=13441
10846 assert(imm
.length
== 1);
10847 assert(equal(imm
, imm
[]));
10848 assert(equal(imm
, imm
[0 .. 1]));
10849 assert(equal(imm
[0 .. 0], imme
));
10850 assert(equal(imm
[1 .. 1], imme
));
10851 assert(imm
[0] == 1);
10854 // Tests multiple-element results
10857 import std
.algorithm
.comparison
: equal
;
10858 import std
.algorithm
.iteration
: joiner
;
10859 import std
.meta
: AliasSeq
;
10860 static assert(!__traits(compiles
, only(1, "1")));
10862 auto nums
= only
!(byte, uint, long)(1, 2, 3);
10863 static assert(is(ElementType
!(typeof(nums
)) == long));
10864 assert(nums
.length
== 3);
10866 foreach (i
; 0 .. 3)
10867 assert(nums
[i
] == i
+ 1);
10869 auto saved
= nums
.save
;
10871 foreach (i
; 1 .. 4)
10873 assert(nums
.front
== nums
[0]);
10874 assert(nums
.front
== i
);
10876 assert(nums
.length
== 3 - i
);
10879 assert(nums
.empty
);
10881 assert(saved
.equal(only(1, 2, 3)));
10882 assert(saved
.equal(saved
[]));
10883 assert(saved
[0 .. 1].equal(only(1)));
10884 assert(saved
[0 .. 2].equal(only(1, 2)));
10885 assert(saved
[0 .. 3].equal(saved
));
10886 assert(saved
[1 .. 3].equal(only(2, 3)));
10887 assert(saved
[2 .. 3].equal(only(3)));
10888 assert(saved
[0 .. 0].empty
);
10889 assert(saved
[3 .. 3].empty
);
10891 alias data
= AliasSeq
!("one", "two", "three", "four");
10893 ["one two", "one two three", "one two three four"];
10894 string
[] joinedRange
= joined
;
10896 static foreach (argCount
; 2 .. 5)
10898 auto values
= only(data
[0 .. argCount
]);
10899 alias Values
= typeof(values
);
10900 static assert(is(ElementType
!Values
== string
));
10901 static assert(isInputRange
!Values
);
10902 static assert(isForwardRange
!Values
);
10903 static assert(isBidirectionalRange
!Values
);
10904 static assert(isRandomAccessRange
!Values
);
10905 static assert(hasSlicing
!Values
);
10906 static assert(hasLength
!Values
);
10908 assert(values
.length
== argCount
);
10909 assert(values
[0 .. $].equal(values
[0 .. values
.length
]));
10910 assert(values
.joiner(" ").equal(joinedRange
.front
));
10911 joinedRange
.popFront();
10914 assert(saved
.retro
.equal(only(3, 2, 1)));
10915 assert(saved
.length
== 3);
10917 assert(saved
.back
== 3);
10919 assert(saved
.length
== 2);
10920 assert(saved
.back
== 2);
10922 assert(saved
.front
== 1);
10924 assert(saved
.length
== 1);
10925 assert(saved
.front
== 2);
10928 assert(saved
.empty
);
10930 auto imm
= only
!(immutable int, immutable int)(42, 24);
10931 alias Imm
= typeof(imm
);
10932 static assert(is(ElementType
!Imm
== immutable(int)));
10933 assert(!imm
.empty
);
10934 assert(imm
.init
.empty
); // https://issues.dlang.org/show_bug.cgi?id=13441
10935 assert(imm
.front
== 42);
10937 assert(imm
.front
== 24);
10941 static struct Test
{ int* a
; }
10942 immutable(Test
) test;
10943 cast(void) only(test, test); // Works with mutable indirection
10946 // https://issues.dlang.org/show_bug.cgi?id=21129
10949 auto range
= () @safe {
10950 const(char)[5] staticStr
= "Hello";
10952 // `only` must store a char[5] - not a char[]!
10953 return only(staticStr
, " World");
10956 assert(range
.join
== "Hello World");
10959 // https://issues.dlang.org/show_bug.cgi?id=21129
10962 struct AliasedString
10964 const(char)[5] staticStr
= "Hello";
10966 @property const(char)[] slice() const
10968 return staticStr
[];
10973 auto range
= () @safe {
10974 auto hello
= AliasedString();
10976 // a copy of AliasedString is stored in the range.
10977 return only(hello
, " World");
10980 assert(range
.join
== "Hello World");
10983 // https://issues.dlang.org/show_bug.cgi?id=21022
10984 @safe pure nothrow unittest
10992 immutable(S
)[] arr
;
10993 auto r1
= arr
.chain(x
.only
, only(x
, x
));
10996 // https://issues.dlang.org/show_bug.cgi?id=24382
10999 auto r1
= only(123);
11004 auto r2
= only(123, 456);
11009 auto r3
= only(1.23, 456);
11010 // Can't assign double to int
11011 static assert(!__traits(compiles
, r3
.front
= 7.89));
11012 static assert(!__traits(compiles
, r3
.back
= 7.89));
11013 // Workaround https://issues.dlang.org/show_bug.cgi?id=24383
11014 static assert(!__traits(compiles
, () { r3
[0] = 7.89; }));
11015 // Can't assign type other than element type (even if compatible)
11016 static assert(!__traits(compiles
, r3
.front
= 789));
11017 static assert(!__traits(compiles
, r3
.back
= 789));
11018 // Workaround https://issues.dlang.org/show_bug.cgi?id=24383
11019 static assert(!__traits(compiles
, () { r3
[0] = 789; }));
11023 Iterate over `range` with an attached index variable.
11025 Each element is a $(REF Tuple, std,typecons) containing the index
11026 and the element, in that order, where the index member is named `index`
11027 and the element member is named `value`.
11029 The index starts at `start` and is incremented by one on every iteration.
11032 If `range` has length, then it is an error to pass a value for `start`
11033 so that `start + range.length` is bigger than `Enumerator.max`, thus
11034 it is ensured that overflow cannot happen.
11036 If `range` does not have length, and `popFront` is called when
11037 `front.index == Enumerator.max`, the index will overflow and
11038 continue from `Enumerator.min`.
11041 range = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to attach indexes to
11042 start = the number to start the index counter from
11045 At minimum, an input range. All other range primitives are given in the
11046 resulting range if `range` has them. The exceptions are the bidirectional
11047 primitives, which are propagated only if `range` has length.
11050 Useful for using `foreach` with an index loop variable:
11052 import std.stdio : stdin, stdout;
11053 import std.range : enumerate;
11055 foreach (lineNum, line; stdin.byLine().enumerate(1))
11056 stdout.writefln("line #%s: %s", lineNum, line);
11059 auto enumerate(Enumerator
= size_t
, Range
)(Range range
, Enumerator start
= 0)
11060 if (isIntegral
!Enumerator
&& isInputRange
!Range
)
11063 static if (hasLength
!Range
)
11065 // TODO: core.checkedint supports mixed signedness yet?
11066 import core
.checkedint
: adds
, addu
;
11067 import std
.conv
: ConvException
, to
;
11068 import std
.traits
: isSigned
, Largest
, Signed
;
11070 alias LengthType
= typeof(range
.length
);
11072 static if (isSigned
!Enumerator
&& isSigned
!LengthType
)
11073 auto result
= adds(start
, range
.length
, overflow
);
11074 else static if (isSigned
!Enumerator
)
11076 alias signed_t
= Largest
!(Enumerator
, Signed
!LengthType
);
11077 signed_t signedLength
;
11078 //This is to trick the compiler because if length is enum
11079 //the compiler complains about unreachable code.
11082 return range
.length
;
11084 //Can length fit in the signed type
11085 assert(getLength() < signed_t
.max
,
11086 "a signed length type is required but the range's length() is too great");
11087 signedLength
= range
.length
;
11088 auto result
= adds(start
, signedLength
, overflow
);
11092 static if (isSigned
!LengthType
)
11093 assert(range
.length
>= 0);
11094 auto result
= addu(start
, range
.length
, overflow
);
11097 assert(!overflow
&& result
<= Enumerator
.max
);
11102 // TODO: Relax isIntegral!Enumerator to allow user-defined integral types
11103 static struct Result
11105 import std
.typecons
: Tuple
;
11108 alias ElemType
= Tuple
!(Enumerator
, "index", ElementType
!Range
, "value");
11110 Unqual
!Enumerator index
;
11113 ElemType
front() @property
11115 assert(!range
.empty
, "Attempting to fetch the front of an empty enumerate");
11116 return typeof(return)(index
, range
.front
);
11119 static if (isInfinite
!Range
)
11120 enum bool empty
= false;
11123 bool empty() @property
11125 return range
.empty
;
11131 assert(!range
.empty
, "Attempting to popFront an empty enumerate");
11133 ++index
; // When !hasLength!Range, overflow is expected
11136 static if (isForwardRange
!Range
)
11138 Result
save() @property
11140 return typeof(return)(range
.save
, index
);
11144 static if (hasLength
!Range
)
11146 mixin ImplementLength
!range
;
11148 static if (isBidirectionalRange
!Range
)
11150 ElemType
back() @property
11152 assert(!range
.empty
, "Attempting to fetch the back of an empty enumerate");
11153 return typeof(return)(cast(Enumerator
)(index
+ range
.length
- 1), range
.back
);
11158 assert(!range
.empty
, "Attempting to popBack an empty enumerate");
11164 static if (isRandomAccessRange
!Range
)
11166 ElemType
opIndex(size_t i
)
11168 return typeof(return)(cast(Enumerator
)(index
+ i
), range
[i
]);
11172 static if (hasSlicing
!Range
)
11174 static if (hasLength
!Range
)
11176 Result
opSlice(size_t i
, size_t j
)
11178 return typeof(return)(range
[i
.. j
], cast(Enumerator
)(index
+ i
));
11183 static struct DollarToken
{}
11184 enum opDollar
= DollarToken
.init
;
11186 Result
opSlice(size_t i
, DollarToken
)
11188 return typeof(return)(range
[i
.. $], cast(Enumerator
)(index
+ i
));
11191 auto opSlice(size_t i
, size_t j
)
11193 return this[i
.. $].takeExactly(j
- 1);
11199 return Result(range
, start
);
11202 /// Can start enumeration from a negative position:
11203 pure @safe nothrow unittest
11205 import std
.array
: assocArray
;
11206 import std
.range
: enumerate
;
11208 bool[int] aa
= true.repeat(3).enumerate(-1).assocArray();
11214 // Make sure passing qualified types works
11215 pure @safe nothrow unittest
11218 immutable start
= 2;
11219 v
[2 .. $].enumerate(start
);
11222 pure @safe nothrow unittest
11224 import std
.internal
.test.dummyrange
: AllDummyRanges
;
11225 import std
.meta
: AliasSeq
;
11226 import std
.typecons
: tuple
;
11228 static struct HasSlicing
11230 typeof(this) front() @property { return typeof(this).init
; }
11231 bool empty() @property { return true; }
11234 typeof(this) opSlice(size_t
, size_t
)
11236 return typeof(this)();
11240 static foreach (DummyType
; AliasSeq
!(AllDummyRanges
, HasSlicing
))
11242 alias R
= typeof(enumerate(DummyType
.init
));
11243 static assert(isInputRange
!R
);
11244 static assert(isForwardRange
!R
== isForwardRange
!DummyType
);
11245 static assert(isRandomAccessRange
!R
== isRandomAccessRange
!DummyType
);
11246 static assert(!hasAssignableElements
!R
);
11248 static if (hasLength
!DummyType
)
11250 static assert(hasLength
!R
);
11251 static assert(isBidirectionalRange
!R
==
11252 isBidirectionalRange
!DummyType
);
11255 static assert(hasSlicing
!R
== hasSlicing
!DummyType
);
11258 static immutable values
= ["zero", "one", "two", "three"];
11259 auto enumerated
= values
[].enumerate();
11260 assert(!enumerated
.empty
);
11261 assert(enumerated
.front
== tuple(0, "zero"));
11262 assert(enumerated
.back
== tuple(3, "three"));
11264 typeof(enumerated
) saved
= enumerated
.save
;
11266 assert(enumerated
.front
== tuple(0, "zero"));
11267 assert(saved
.front
== tuple(1, "one"));
11268 assert(saved
.length
== enumerated
.length
- 1);
11270 assert(enumerated
.back
== tuple(3, "three"));
11271 assert(saved
.back
== tuple(2, "two"));
11273 assert(saved
.front
== tuple(2, "two"));
11274 assert(saved
.back
== tuple(2, "two"));
11276 assert(saved
.empty
);
11278 size_t control
= 0;
11279 foreach (i
, v
; enumerated
)
11281 static assert(is(typeof(i
) == size_t
));
11282 static assert(is(typeof(v
) == typeof(values
[0])));
11283 assert(i
== control
);
11284 assert(v
== values
[i
]);
11285 assert(tuple(i
, v
) == enumerated
[i
]);
11289 assert(enumerated
[0 .. $].front
== tuple(0, "zero"));
11290 assert(enumerated
[$ - 1 .. $].front
== tuple(3, "three"));
11292 foreach (i
; 0 .. 10)
11294 auto shifted
= values
[0 .. 2].enumerate(i
);
11295 assert(shifted
.front
== tuple(i
, "zero"));
11296 assert(shifted
[0] == shifted
.front
);
11298 auto next
= tuple(i
+ 1, "one");
11299 assert(shifted
[1] == next
);
11300 shifted
.popFront();
11301 assert(shifted
.front
== next
);
11302 shifted
.popFront();
11303 assert(shifted
.empty
);
11306 static foreach (T
; AliasSeq
!(ubyte, byte, uint, int))
11308 auto inf
= 42.repeat().enumerate(T
.max
);
11309 alias Inf
= typeof(inf
);
11310 static assert(isInfinite
!Inf
);
11311 static assert(hasSlicing
!Inf
);
11314 assert(inf
.front
== tuple(T
.max
, 42));
11316 assert(inf
.front
== tuple(T
.min
, 42));
11319 inf
= inf
[42 .. $];
11320 assert(inf
.front
== tuple(T
.min
+ 42, 42));
11321 auto window
= inf
[0 .. 2];
11322 assert(window
.length
== 1);
11323 assert(window
.front
== inf
.front
);
11325 assert(window
.empty
);
11329 pure @safe unittest
11331 import std
.algorithm
.comparison
: equal
;
11332 import std
.meta
: AliasSeq
;
11333 static immutable int[] values
= [0, 1, 2, 3, 4];
11334 static foreach (T
; AliasSeq
!(ubyte, ushort, uint, ulong))
11336 auto enumerated
= values
.enumerate
!T();
11337 static assert(is(typeof(enumerated
.front
.index
) == T
));
11338 assert(enumerated
.equal(values
[].zip(values
)));
11340 foreach (T i
; 0 .. 5)
11342 auto subset
= values
[cast(size_t
) i
.. $];
11343 auto offsetEnumerated
= subset
.enumerate(i
);
11344 static assert(is(typeof(enumerated
.front
.index
) == T
));
11345 assert(offsetEnumerated
.equal(subset
.zip(subset
)));
11349 @nogc @safe unittest
11351 const val
= iota(1, 100).enumerate(1);
11353 @nogc @safe unittest
11355 import core
.exception
: AssertError
;
11356 import std
.exception
: assertThrown
;
11357 struct RangePayload
{
11358 enum length
= size_t
.max
;
11360 int front() { return 0; }
11361 bool empty() { return true; }
11363 RangePayload thePayload
;
11364 //Assertion won't happen when contracts are disabled for -release.
11365 debug assertThrown
!AssertError(enumerate(thePayload
, -10));
11367 // https://issues.dlang.org/show_bug.cgi?id=10939
11370 // Re-enable (or remove) if 10939 is resolved.
11371 /+pure+/ @safe unittest // Impure because of std.conv.to
11373 import core
.exception
: RangeError
;
11374 import std
.exception
: assertNotThrown
, assertThrown
;
11375 import std
.meta
: AliasSeq
;
11377 static immutable values
= [42];
11379 static struct SignedLengthRange
11381 immutable(int)[] _values
= values
;
11383 int front() @property { assert(false); }
11384 bool empty() @property { assert(false); }
11385 void popFront() { assert(false); }
11387 int length() @property
11389 return cast(int)_values
.length
;
11393 SignedLengthRange svalues
;
11394 static foreach (Enumerator
; AliasSeq
!(ubyte, byte, ushort, short, uint, int, ulong, long))
11396 assertThrown
!RangeError(values
[].enumerate
!Enumerator(Enumerator
.max
));
11397 assertNotThrown
!RangeError(values
[].enumerate
!Enumerator(Enumerator
.max
- values
.length
));
11398 assertThrown
!RangeError(values
[].enumerate
!Enumerator(Enumerator
.max
- values
.length
+ 1));
11400 assertThrown
!RangeError(svalues
.enumerate
!Enumerator(Enumerator
.max
));
11401 assertNotThrown
!RangeError(svalues
.enumerate
!Enumerator(Enumerator
.max
- values
.length
));
11402 assertThrown
!RangeError(svalues
.enumerate
!Enumerator(Enumerator
.max
- values
.length
+ 1));
11405 static foreach (Enumerator
; AliasSeq
!(byte, short, int))
11407 assertThrown
!RangeError(repeat(0, uint.max
).enumerate
!Enumerator());
11410 assertNotThrown
!RangeError(repeat(0, uint.max
).enumerate
!long());
11415 Returns true if `fn` accepts variables of type T1 and T2 in any order.
11416 The following code should compile:
11418 (ref T1 a, ref T2 b)
11425 template isTwoWayCompatible(alias fn
, T1
, T2
)
11427 enum isTwoWayCompatible
= is(typeof((ref T1 a
, ref T2 b
)
11429 cast(void) fn(a
, b
);
11430 cast(void) fn(b
, a
);
11438 void func1(int a
, int b
);
11439 void func2(int a
, float b
);
11441 static assert(isTwoWayCompatible
!(func1
, int, int));
11442 static assert(isTwoWayCompatible
!(func1
, short, int));
11443 static assert(!isTwoWayCompatible
!(func2
, int, float));
11445 void func3(ref int a
, ref int b
);
11446 static assert( isTwoWayCompatible
!(func3
, int, int));
11447 static assert(!isTwoWayCompatible
!(func3
, short, int));
11452 Policy used with the searching primitives `lowerBound`, $(D
11453 upperBound), and `equalRange` of $(LREF SortedRange) below.
11458 Searches in a linear fashion.
11463 Searches with a step that is grows linearly (1, 2, 3,...)
11464 leading to a quadratic search schedule (indexes tried are 0, 1,
11465 3, 6, 10, 15, 21, 28,...) Once the search overshoots its target,
11466 the remaining interval is searched using binary search. The
11467 search is completed in $(BIGOH sqrt(n)) time. Use it when you
11468 are reasonably confident that the value is around the beginning
11474 Performs a $(LINK2 https://en.wikipedia.org/wiki/Exponential_search,
11475 galloping search algorithm), i.e. searches
11476 with a step that doubles every time, (1, 2, 4, 8, ...) leading
11477 to an exponential search schedule (indexes tried are 0, 1, 3,
11478 7, 15, 31, 63,...) Once the search overshoots its target, the
11479 remaining interval is searched using binary search. A value is
11480 found in $(BIGOH log(n)) time.
11485 Searches using a classic interval halving policy. The search
11486 starts in the middle of the range, and each search step cuts
11487 the range in half. This policy finds a value in $(BIGOH log(n))
11488 time but is less cache friendly than `gallop` for large
11489 ranges. The `binarySearch` policy is used as the last step
11490 of `trot`, `gallop`, `trotBackwards`, and $(D
11491 gallopBackwards) strategies.
11496 Similar to `trot` but starts backwards. Use it when
11497 confident that the value is around the end of the range.
11502 Similar to `gallop` but starts backwards. Use it when
11503 confident that the value is around the end of the range.
11511 import std
.algorithm
.comparison
: equal
;
11513 auto a
= assumeSorted([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
11514 auto p1
= a
.upperBound
!(SearchPolicy
.binarySearch
)(3);
11515 assert(p1
.equal([4, 5, 6, 7, 8, 9]));
11517 auto p2
= a
.lowerBound
!(SearchPolicy
.gallop
)(4);
11518 assert(p2
.equal([0, 1, 2, 3]));
11522 Options for $(LREF SortedRange) ranges (below).
11524 enum SortedRangeOptions
11527 Assume, that the range is sorted without checking.
11532 All elements of the range are checked to be sorted.
11533 The check is performed in O(n) time.
11538 Some elements of the range are checked to be sorted.
11539 For ranges with random order, this will almost surely
11540 detect, that it is not sorted. For almost sorted ranges
11541 it's more likely to fail. The checked elements are choosen
11542 in a deterministic manner, which makes this check reproducable.
11543 The check is performed in O(log(n)) time.
11549 @safe pure unittest
11551 // create a SortedRange, that's checked strictly
11552 SortedRange
!(int[],"a < b", SortedRangeOptions
.checkStrictly
)([ 1, 3, 5, 7, 9 ]);
11556 Represents a sorted range. In addition to the regular range
11557 primitives, supports additional operations that take advantage of the
11558 ordering, such as merge and binary search. To obtain a $(D
11559 SortedRange) from an unsorted range `r`, use
11560 $(REF sort, std,algorithm,sorting) which sorts `r` in place and returns the
11561 corresponding `SortedRange`. To construct a `SortedRange` from a range
11562 `r` that is known to be already sorted, use $(LREF assumeSorted).
11565 pred: The predicate used to define the sortedness
11566 opt: Controls how strongly the range is checked for sortedness.
11567 Will only be used for `RandomAccessRanges`.
11568 Will not be used in CTFE.
11570 struct SortedRange(Range
, alias pred
= "a < b",
11571 SortedRangeOptions opt
= SortedRangeOptions
.assumeSorted
)
11572 if (isInputRange
!Range
&& !isInstanceOf
!(SortedRange
, Range
))
11574 import std
.functional
: binaryFun
;
11576 private alias predFun
= binaryFun
!pred
;
11577 private bool geq(L
, R
)(L lhs
, R rhs
)
11579 return !predFun(lhs
, rhs
);
11581 private bool gt(L
, R
)(L lhs
, R rhs
)
11583 return predFun(rhs
, lhs
);
11585 private Range _input
;
11587 // Undocummented because a clearer way to invoke is by calling
11591 static if (opt
== SortedRangeOptions
.checkRoughly
)
11593 roughlyVerifySorted(input
);
11595 static if (opt
== SortedRangeOptions
.checkStrictly
)
11597 strictlyVerifySorted(input
);
11599 this._input
= input
;
11603 static if (opt
== SortedRangeOptions
.checkRoughly
)
11604 private void roughlyVerifySorted(Range r
)
11608 static if (isRandomAccessRange
!Range
&& hasLength
!Range
)
11610 import core
.bitop
: bsr;
11611 import std
.algorithm
.sorting
: isSorted
;
11612 import std
.exception
: enforce
;
11614 // Check the sortedness of the input
11615 if (r
.length
< 2) return;
11617 immutable size_t msb
= bsr(r
.length
) + 1;
11618 assert(msb
> 0 && msb
<= r
.length
);
11619 immutable step
= r
.length
/ msb
;
11620 auto st
= stride(r
, step
);
11622 enforce(isSorted
!pred(st
), "Range is not sorted");
11628 static if (opt
== SortedRangeOptions
.checkStrictly
)
11629 private void strictlyVerifySorted(Range r
)
11633 static if (isRandomAccessRange
!Range
&& hasLength
!Range
)
11635 import std
.algorithm
.sorting
: isSorted
;
11636 import std
.exception
: enforce
;
11638 enforce(isSorted
!pred(r
), "Range is not sorted");
11643 /// Range primitives.
11644 @property bool empty() //const
11646 return this._input
.empty
;
11650 static if (isForwardRange
!Range
)
11651 @property auto save()
11653 // Avoid the constructor
11654 typeof(this) result
= this;
11655 result
._input
= _input
.save
;
11660 @property auto ref front()
11662 return _input
.front
;
11672 static if (isBidirectionalRange
!Range
)
11674 @property auto ref back()
11676 return _input
.back
;
11687 static if (isRandomAccessRange
!Range
)
11688 auto ref opIndex(size_t i
)
11694 static if (hasSlicing
!Range
)
11695 auto opSlice(size_t a
, size_t b
) return scope
11699 "Attempting to slice a SortedRange with a larger first argument than the second."
11701 typeof(this) result
= this;
11702 result
._input
= _input
[a
.. b
];// skip checking
11706 mixin ImplementLength
!_input
;
11709 Releases the controlled range and returns it.
11711 This does the opposite of $(LREF assumeSorted): instead of turning a range
11712 into a `SortedRange`, it extracts the original range back out of the `SortedRange`
11713 using $(REF, move, std,algorithm,mutation).
11715 auto release() return scope
11717 import std
.algorithm
.mutation
: move
;
11718 return move(_input
);
11722 static if (is(Range
: int[]))
11725 import std
.algorithm
.sorting
: sort
;
11726 int[3] data
= [ 1, 2, 3 ];
11727 auto a
= assumeSorted(data
[]);
11728 assert(a
== sort
!"a < b"(data
[]));
11729 int[] p
= a
.release();
11730 assert(p
== [ 1, 2, 3 ]);
11733 // Assuming a predicate "test" that returns 0 for a left portion
11734 // of the range and then 1 for the rest, returns the index at
11735 // which the first 1 appears. Used internally by the search routines.
11736 private size_t
getTransitionIndex(SearchPolicy sp
, alias test, V
)(V v
)
11737 if (sp
== SearchPolicy
.binarySearch
&& isRandomAccessRange
!Range
&& hasLength
!Range
)
11739 size_t first
= 0, count
= _input
.length
;
11742 immutable step
= count
/ 2, it
= first
+ step
;
11743 if (!test(_input
[it
], v
))
11756 // Specialization for trot and gallop
11757 private size_t
getTransitionIndex(SearchPolicy sp
, alias test, V
)(V v
)
11758 if ((sp
== SearchPolicy
.trot || sp
== SearchPolicy
.gallop
)
11759 && isRandomAccessRange
!Range
)
11761 if (empty ||
test(front
, v
)) return 0;
11762 immutable count
= length
;
11763 if (count
== 1) return 1;
11764 size_t below
= 0, above
= 1, step
= 2;
11765 while (!test(_input
[above
], v
))
11767 // Still too small, update below and increase gait
11769 immutable next
= above
+ step
;
11772 // Overshot - the next step took us beyond the end. So
11773 // now adjust next and simply exit the loop to do the
11774 // binary search thingie.
11778 // Still in business, increase step and continue
11780 static if (sp
== SearchPolicy
.trot
)
11785 return below
+ this[below
.. above
].getTransitionIndex
!(
11786 SearchPolicy
.binarySearch
, test, V
)(v
);
11789 // Specialization for trotBackwards and gallopBackwards
11790 private size_t
getTransitionIndex(SearchPolicy sp
, alias test, V
)(V v
)
11791 if ((sp
== SearchPolicy
.trotBackwards || sp
== SearchPolicy
.gallopBackwards
)
11792 && isRandomAccessRange
!Range
)
11794 immutable count
= length
;
11795 if (empty ||
!test(back
, v
)) return count
;
11796 if (count
== 1) return 0;
11797 size_t below
= count
- 2, above
= count
- 1, step
= 2;
11798 while (test(_input
[below
], v
))
11800 // Still too large, update above and increase gait
11804 // Overshot - the next step took us beyond the end. So
11805 // now adjust next and simply fall through to do the
11806 // binary search thingie.
11810 // Still in business, increase step and continue
11812 static if (sp
== SearchPolicy
.trot
)
11817 return below
+ this[below
.. above
].getTransitionIndex
!(
11818 SearchPolicy
.binarySearch
, test, V
)(v
);
11823 This function uses a search with policy `sp` to find the
11824 largest left subrange on which $(D pred(x, value)) is `true` for
11825 all `x` (e.g., if `pred` is "less than", returns the portion of
11826 the range with elements strictly smaller than `value`). The search
11827 schedule and its complexity are documented in
11828 $(LREF SearchPolicy).
11830 auto lowerBound(SearchPolicy sp
= SearchPolicy
.binarySearch
, V
)(V value
)
11831 if (isTwoWayCompatible
!(predFun
, ElementType
!Range
, V
)
11832 && hasSlicing
!Range
)
11834 return this[0 .. getTransitionIndex
!(sp
, geq
)(value
)];
11838 static if (is(Range
: int[]))
11841 import std
.algorithm
.comparison
: equal
;
11842 auto a
= assumeSorted([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]);
11843 auto p
= a
.lowerBound(4);
11844 assert(equal(p
, [ 0, 1, 2, 3 ]));
11849 This function searches with policy `sp` to find the largest right
11850 subrange on which $(D pred(value, x)) is `true` for all `x`
11851 (e.g., if `pred` is "less than", returns the portion of the range
11852 with elements strictly greater than `value`). The search schedule
11853 and its complexity are documented in $(LREF SearchPolicy).
11855 For ranges that do not offer random access, `SearchPolicy.linear`
11856 is the only policy allowed (and it must be specified explicitly lest it exposes
11857 user code to unexpected inefficiencies). For random-access searches, all
11858 policies are allowed, and `SearchPolicy.binarySearch` is the default.
11860 auto upperBound(SearchPolicy sp
= SearchPolicy
.binarySearch
, V
)(V value
)
11861 if (isTwoWayCompatible
!(predFun
, ElementType
!Range
, V
))
11863 static assert(hasSlicing
!Range || sp
== SearchPolicy
.linear
,
11864 "Specify SearchPolicy.linear explicitly for "
11865 ~ typeof(this).stringof
);
11866 static if (sp
== SearchPolicy
.linear
)
11868 for (; !_input
.empty
&& !predFun(value
, _input
.front
);
11876 return this[getTransitionIndex
!(sp
, gt
)(value
) .. length
];
11881 static if (is(Range
: int[]))
11884 import std
.algorithm
.comparison
: equal
;
11885 auto a
= assumeSorted([ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]);
11886 auto p
= a
.upperBound(3);
11887 assert(equal(p
, [4, 4, 5, 6]));
11893 Returns the subrange containing all elements `e` for which both $(D
11894 pred(e, value)) and $(D pred(value, e)) evaluate to `false` (e.g.,
11895 if `pred` is "less than", returns the portion of the range with
11896 elements equal to `value`). Uses a classic binary search with
11897 interval halving until it finds a value that satisfies the condition,
11898 then uses `SearchPolicy.gallopBackwards` to find the left boundary
11899 and `SearchPolicy.gallop` to find the right boundary. These
11900 policies are justified by the fact that the two boundaries are likely
11901 to be near the first found value (i.e., equal ranges are relatively
11902 small). Completes the entire search in $(BIGOH log(n)) time.
11904 auto equalRange(V
)(V value
)
11905 if (isTwoWayCompatible
!(predFun
, ElementType
!Range
, V
)
11906 && isRandomAccessRange
!Range
)
11908 size_t first
= 0, count
= _input
.length
;
11911 immutable step
= count
/ 2;
11912 auto it
= first
+ step
;
11913 if (predFun(_input
[it
], value
))
11915 // Less than value, bump left bound up
11919 else if (predFun(value
, _input
[it
]))
11921 // Greater than value, chop count
11926 // Equal to value, do binary searches in the
11927 // leftover portions
11928 // Gallop towards the left end as it's likely nearby
11929 immutable left
= first
11930 + this[first
.. it
]
11931 .lowerBound
!(SearchPolicy
.gallopBackwards
)(value
).length
;
11933 // Gallop towards the right end as it's likely nearby
11934 immutable right
= first
11935 - this[it
+ 1 .. first
]
11936 .upperBound
!(SearchPolicy
.gallop
)(value
).length
;
11937 return this[left
.. right
];
11944 static if (is(Range
: int[]))
11947 import std
.algorithm
.comparison
: equal
;
11948 auto a
= [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ];
11949 auto r
= a
.assumeSorted
.equalRange(3);
11950 assert(equal(r
, [ 3, 3, 3 ]));
11955 Returns a tuple `r` such that `r[0]` is the same as the result
11956 of `lowerBound(value)`, `r[1]` is the same as the result of $(D
11957 equalRange(value)), and `r[2]` is the same as the result of $(D
11958 upperBound(value)). The call is faster than computing all three
11959 separately. Uses a search schedule similar to $(D
11960 equalRange). Completes the entire search in $(BIGOH log(n)) time.
11962 auto trisect(V
)(V value
)
11963 if (isTwoWayCompatible
!(predFun
, ElementType
!Range
, V
)
11964 && isRandomAccessRange
!Range
&& hasLength
!Range
)
11966 import std
.typecons
: tuple
;
11967 size_t first
= 0, count
= _input
.length
;
11970 immutable step
= count
/ 2;
11971 auto it
= first
+ step
;
11972 if (predFun(_input
[it
], value
))
11974 // Less than value, bump left bound up
11978 else if (predFun(value
, _input
[it
]))
11980 // Greater than value, chop count
11985 // Equal to value, do binary searches in the
11986 // leftover portions
11987 // Gallop towards the left end as it's likely nearby
11988 immutable left
= first
11989 + this[first
.. it
]
11990 .lowerBound
!(SearchPolicy
.gallopBackwards
)(value
).length
;
11992 // Gallop towards the right end as it's likely nearby
11993 immutable right
= first
11994 - this[it
+ 1 .. first
]
11995 .upperBound
!(SearchPolicy
.gallop
)(value
).length
;
11996 return tuple(this[0 .. left
], this[left
.. right
],
11997 this[right
.. length
]);
12000 // No equal element was found
12001 return tuple(this[0 .. first
], this.init
, this[first
.. length
]);
12005 static if (is(Range
: int[]))
12008 import std
.algorithm
.comparison
: equal
;
12009 auto a
= [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ];
12010 auto r
= assumeSorted(a
).trisect(3);
12011 assert(equal(r
[0], [ 1, 2 ]));
12012 assert(equal(r
[1], [ 3, 3, 3 ]));
12013 assert(equal(r
[2], [ 4, 4, 5, 6 ]));
12018 Returns `true` if and only if `value` can be found in $(D
12019 range), which is assumed to be sorted. Performs $(BIGOH log(r.length))
12020 evaluations of `pred`.
12023 bool contains(V
)(V value
)
12024 if (isRandomAccessRange
!Range
)
12026 if (empty
) return false;
12027 immutable i
= getTransitionIndex
!(SearchPolicy
.binarySearch
, geq
)(value
);
12028 if (i
>= length
) return false;
12029 return !predFun(value
, _input
[i
]);
12033 Like `contains`, but the value is specified before the range.
12035 bool opBinaryRight(string op
, V
)(V value
)
12036 if (op
== "in" && isRandomAccessRange
!Range
)
12038 return contains(value
);
12043 Returns a range of subranges of elements that are equivalent according to the
12048 import std
.algorithm
.iteration
: chunkBy
;
12049 return _input
.chunkBy
!((a
, b
) => !predFun(a
, b
) && !predFun(b
, a
));
12054 template SortedRange(Range
, alias pred
= "a < b",
12055 SortedRangeOptions opt
= SortedRangeOptions
.assumeSorted
)
12056 if (isInstanceOf
!(SortedRange
, Range
))
12058 // Avoid nesting SortedRange types (see https://issues.dlang.org/show_bug.cgi?id=18933);
12059 alias SortedRange
= SortedRange
!(Unqual
!(typeof(Range
._input
)), pred
, opt
);
12065 import std
.algorithm
.sorting
: sort
;
12066 auto a
= [ 1, 2, 3, 42, 52, 64 ];
12067 auto r
= assumeSorted(a
);
12068 assert(r
.contains(3));
12069 assert(!(32 in r
));
12070 auto r1
= sort
!"a > b"(a
);
12072 assert(!r1
.contains(32));
12073 assert(r1
.release() == [ 64, 52, 42, 3, 2, 1 ]);
12077 `SortedRange` could accept ranges weaker than random-access, but it
12078 is unable to provide interesting functionality for them. Therefore,
12079 `SortedRange` is currently restricted to random-access ranges.
12081 No copy of the original range is ever made. If the underlying range is
12082 changed concurrently with its corresponding `SortedRange` in ways
12083 that break its sorted-ness, `SortedRange` will work erratically.
12087 import std
.algorithm
.mutation
: swap
;
12088 auto a
= [ 1, 2, 3, 42, 52, 64 ];
12089 auto r
= assumeSorted(a
);
12090 assert(r
.contains(42));
12091 swap(a
[3], a
[5]); // illegal to break sortedness of original range
12092 assert(!r
.contains(42)); // passes although it shouldn't
12096 `SortedRange` can be searched with predicates that do not take
12097 two elements of the underlying range as arguments.
12099 This is useful, if a range of structs is sorted by a member and you
12100 want to search in that range by only providing a value for that member.
12105 import std
.algorithm
.comparison
: equal
;
12106 static struct S
{ int i
; }
12107 static bool byI(A
, B
)(A a
, B b
)
12109 static if (is(A
== S
))
12114 auto r
= assumeSorted
!byI([S(1), S(2), S(3)]);
12115 auto lessThanTwo
= r
.lowerBound(2);
12116 assert(equal(lessThanTwo
, [S(1)]));
12121 import std
.exception
: assertThrown
, assertNotThrown
;
12123 assertNotThrown(SortedRange
!(int[])([ 1, 3, 10, 5, 7 ]));
12124 assertThrown(SortedRange
!(int[],"a < b", SortedRangeOptions
.checkStrictly
)([ 1, 3, 10, 5, 7 ]));
12126 // these two checks are implementation depended
12127 assertNotThrown(SortedRange
!(int[],"a < b", SortedRangeOptions
.checkRoughly
)([ 1, 3, 10, 5, 12, 2 ]));
12128 assertThrown(SortedRange
!(int[],"a < b", SortedRangeOptions
.checkRoughly
)([ 1, 3, 10, 5, 2, 12 ]));
12133 import std
.algorithm
.comparison
: equal
;
12135 auto a
= [ 10, 20, 30, 30, 30, 40, 40, 50, 60 ];
12136 auto r
= assumeSorted(a
).trisect(30);
12137 assert(equal(r
[0], [ 10, 20 ]));
12138 assert(equal(r
[1], [ 30, 30, 30 ]));
12139 assert(equal(r
[2], [ 40, 40, 50, 60 ]));
12141 r
= assumeSorted(a
).trisect(35);
12142 assert(equal(r
[0], [ 10, 20, 30, 30, 30 ]));
12143 assert(r
[1].empty
);
12144 assert(equal(r
[2], [ 40, 40, 50, 60 ]));
12149 import std
.algorithm
.comparison
: equal
;
12150 auto a
= [ "A", "AG", "B", "E", "F" ];
12151 auto r
= assumeSorted
!"cmp(a,b) < 0"(a
).trisect("B"w
);
12152 assert(equal(r
[0], [ "A", "AG" ]));
12153 assert(equal(r
[1], [ "B" ]));
12154 assert(equal(r
[2], [ "E", "F" ]));
12155 r
= assumeSorted
!"cmp(a,b) < 0"(a
).trisect("A"d
);
12156 assert(r
[0].empty
);
12157 assert(equal(r
[1], [ "A" ]));
12158 assert(equal(r
[2], [ "AG", "B", "E", "F" ]));
12163 import std
.algorithm
.comparison
: equal
;
12164 static void test(SearchPolicy pol
)()
12166 auto a
= [ 1, 2, 3, 42, 52, 64 ];
12167 auto r
= assumeSorted(a
);
12168 assert(equal(r
.lowerBound(42), [1, 2, 3]));
12170 assert(equal(r
.lowerBound
!(pol
)(42), [1, 2, 3]));
12171 assert(equal(r
.lowerBound
!(pol
)(41), [1, 2, 3]));
12172 assert(equal(r
.lowerBound
!(pol
)(43), [1, 2, 3, 42]));
12173 assert(equal(r
.lowerBound
!(pol
)(51), [1, 2, 3, 42]));
12174 assert(equal(r
.lowerBound
!(pol
)(3), [1, 2]));
12175 assert(equal(r
.lowerBound
!(pol
)(55), [1, 2, 3, 42, 52]));
12176 assert(equal(r
.lowerBound
!(pol
)(420), a
));
12177 assert(equal(r
.lowerBound
!(pol
)(0), a
[0 .. 0]));
12179 assert(equal(r
.upperBound
!(pol
)(42), [52, 64]));
12180 assert(equal(r
.upperBound
!(pol
)(41), [42, 52, 64]));
12181 assert(equal(r
.upperBound
!(pol
)(43), [52, 64]));
12182 assert(equal(r
.upperBound
!(pol
)(51), [52, 64]));
12183 assert(equal(r
.upperBound
!(pol
)(53), [64]));
12184 assert(equal(r
.upperBound
!(pol
)(55), [64]));
12185 assert(equal(r
.upperBound
!(pol
)(420), a
[0 .. 0]));
12186 assert(equal(r
.upperBound
!(pol
)(0), a
));
12189 test!(SearchPolicy
.trot
)();
12190 test!(SearchPolicy
.gallop
)();
12191 test!(SearchPolicy
.trotBackwards
)();
12192 test!(SearchPolicy
.gallopBackwards
)();
12193 test!(SearchPolicy
.binarySearch
)();
12198 // Check for small arrays
12200 auto r
= assumeSorted(a
);
12202 r
= assumeSorted(a
);
12204 r
= assumeSorted(a
);
12206 r
= assumeSorted(a
);
12211 import std
.algorithm
.mutation
: swap
;
12212 auto a
= [ 1, 2, 3, 42, 52, 64 ];
12213 auto r
= assumeSorted(a
);
12214 assert(r
.contains(42));
12215 swap(a
[3], a
[5]); // illegal to break sortedness of original range
12216 assert(!r
.contains(42)); // passes although it shouldn't
12219 @betterC @nogc nothrow @safe unittest
12221 static immutable(int)[] arr
= [ 1, 2, 3 ];
12222 auto s
= assumeSorted(arr
);
12227 import std
.algorithm
.comparison
: equal
;
12228 int[] arr
= [100, 101, 102, 200, 201, 300];
12229 auto s
= assumeSorted
!((a
, b
) => a
/ 100 < b
/ 100)(arr
);
12230 assert(s
.groupBy
.equal
!equal([[100, 101, 102], [200, 201], [300]]));
12233 // Test on an input range
12236 import std
.conv
: text
;
12237 import std
.file
: exists
, remove
, tempDir
;
12238 import std
.path
: buildPath
;
12239 import std
.stdio
: File
;
12240 import std
.uuid
: randomUUID
;
12241 auto name
= buildPath(tempDir(), "test.std.range.line-" ~ text(__LINE__
) ~
12242 "." ~ randomUUID().toString());
12243 auto f
= File(name
, "w");
12244 scope(exit
) if (exists(name
)) remove(name
);
12245 // write a sorted range of lines to the file
12246 f
.write("abc\ndef\nghi\njkl");
12249 auto r
= assumeSorted(f
.byLine());
12250 auto r1
= r
.upperBound
!(SearchPolicy
.linear
)("def");
12251 assert(r1
.front
== "ghi", r1
.front
);
12255 // https://issues.dlang.org/show_bug.cgi?id=19337
12258 import std
.algorithm
.sorting
: sort
;
12259 auto a
= [ 1, 2, 3, 42, 52, 64 ];
12260 a
.sort
.sort
!"a > b";
12264 Assumes `r` is sorted by predicate `pred` and returns the
12265 corresponding $(D SortedRange!(pred, R)) having `r` as support.
12266 To check for sorted-ness at
12267 cost $(BIGOH n), use $(REF isSorted, std,algorithm,sorting).
12269 auto assumeSorted(alias pred
= "a < b", R
)(R r
)
12270 if (isInputRange
!(Unqual
!R
))
12272 // Avoid senseless `SortedRange!(SortedRange!(...), pred)` nesting.
12273 static if (is(R
== SortedRange
!(RRange
, RPred
), RRange
, alias RPred
))
12275 static if (isInputRange
!R
&& __traits(isSame
, pred
, RPred
))
12276 // If the predicate is the same and we don't need to cast away
12277 // constness for the result to be an input range.
12280 return SortedRange
!(Unqual
!(typeof(r
._input
)), pred
)(r
._input
);
12284 return SortedRange
!(Unqual
!R
, pred
)(r
);
12291 import std
.algorithm
.comparison
: equal
;
12293 int[] a
= [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
12294 auto p
= assumeSorted(a
);
12296 assert(equal(p
.lowerBound(4), [0, 1, 2, 3]));
12297 assert(equal(p
.lowerBound(5), [0, 1, 2, 3, 4]));
12298 assert(equal(p
.lowerBound(6), [0, 1, 2, 3, 4, 5]));
12299 assert(equal(p
.lowerBound(6.9), [0, 1, 2, 3, 4, 5, 6]));
12304 import std
.algorithm
.comparison
: equal
;
12305 static assert(isRandomAccessRange
!(SortedRange
!(int[])));
12306 int[] a
= [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ];
12307 auto p
= assumeSorted(a
).upperBound(3);
12308 assert(equal(p
, [4, 4, 5, 6 ]));
12309 p
= assumeSorted(a
).upperBound(4.2);
12310 assert(equal(p
, [ 5, 6 ]));
12312 // https://issues.dlang.org/show_bug.cgi?id=18933
12313 // don't create senselessly nested SortedRange types.
12314 assert(is(typeof(assumeSorted(a
)) == typeof(assumeSorted(assumeSorted(a
)))));
12315 assert(is(typeof(assumeSorted(a
)) == typeof(assumeSorted(assumeSorted
!"a > b"(a
)))));
12320 import std
.algorithm
.comparison
: equal
;
12321 import std
.conv
: text
;
12323 int[] a
= [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ];
12324 auto p
= assumeSorted(a
).equalRange(3);
12325 assert(equal(p
, [ 3, 3, 3 ]), text(p
));
12326 p
= assumeSorted(a
).equalRange(4);
12327 assert(equal(p
, [ 4, 4 ]), text(p
));
12328 p
= assumeSorted(a
).equalRange(2);
12329 assert(equal(p
, [ 2 ]));
12330 p
= assumeSorted(a
).equalRange(0);
12332 p
= assumeSorted(a
).equalRange(7);
12334 p
= assumeSorted(a
).equalRange(3.0);
12335 assert(equal(p
, [ 3, 3, 3]));
12340 int[] a
= [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ];
12343 auto b
= a
[a
.length
/ 2];
12344 //auto r = sort(a);
12345 //assert(r.contains(b));
12351 auto a
= [ 5, 7, 34, 345, 677 ];
12352 auto r
= assumeSorted(a
);
12354 r
= assumeSorted(a
);
12356 r
= assumeSorted(a
);
12359 // https://issues.dlang.org/show_bug.cgi?id=15003
12360 @nogc @safe unittest
12362 static immutable a
= [1, 2, 3, 4];
12363 auto r
= a
.assumeSorted
;
12367 Wrapper which effectively makes it possible to pass a range by reference.
12368 Both the original range and the RefRange will always have the exact same
12369 elements. Any operation done on one will affect the other. So, for instance,
12370 if it's passed to a function which would implicitly copy the original range
12371 if it were passed to it, the original range is $(I not) copied but is
12372 consumed as if it were a reference type.
12375 `save` works as normal and operates on a new range, so if
12376 `save` is ever called on the `RefRange`, then no operations on the
12377 saved range will affect the original.
12380 range = the range to construct the `RefRange` from
12383 A `RefRange`. If the given range is a class type
12384 (and thus is already a reference type), then the original
12385 range is returned rather than a `RefRange`.
12388 if (isInputRange
!R
)
12393 this(R
* range
) @safe pure nothrow
12400 This does not assign the pointer of `rhs` to this `RefRange`.
12401 Rather it assigns the range pointed to by `rhs` to the range pointed
12402 to by this `RefRange`. This is because $(I any) operation on a
12403 `RefRange` is the same is if it occurred to the original range. The
12404 one exception is when a `RefRange` is assigned `null` either
12405 directly or because `rhs` is `null`. In that case, `RefRange`
12406 no longer refers to the original range but is `null`.
12408 auto opAssign(RefRange rhs
)
12410 if (_range
&& rhs
._range
)
12411 *_range
= *rhs
._range
;
12413 _range
= rhs
._range
;
12419 void opAssign(typeof(null) rhs
)
12426 A pointer to the wrapped range.
12428 @property inout(R
*) ptr() @safe inout pure nothrow
12437 @property auto front() {assert(0);}
12439 @property auto front() const {assert(0);}
12441 @property auto front(ElementType
!R value
) {assert(0);}
12445 @property auto front()
12447 return (*_range
).front
;
12450 static if (is(typeof(((const R
* r
) => (*r
).front
)(null)))) @property auto front() const
12452 return (*_range
).front
;
12455 static if (is(typeof((*_range
).front
= (*_range
).front
))) @property auto front(ElementType
!R value
)
12457 return (*_range
).front
= value
;
12464 @property bool empty(); ///
12465 @property bool empty() const; ///Ditto
12467 else static if (isInfinite
!R
)
12468 enum empty
= false;
12471 @property bool empty()
12473 return (*_range
).empty
;
12476 static if (is(typeof(((const R
* r
) => (*r
).empty
)(null)))) @property bool empty() const
12478 return (*_range
).empty
;
12486 return (*_range
).popFront();
12493 Only defined if `isForwardRange!R` is `true`.
12495 @property auto save() {assert(0);}
12497 @property auto save() const {assert(0);}
12499 auto opSlice() {assert(0);}
12501 auto opSlice() const {assert(0);}
12503 else static if (isForwardRange
!R
)
12505 import std
.traits
: isSafe
;
12506 private alias S
= typeof((() => (*_range
).save
)());
12508 static if (is(typeof(((const R
* r
) => (*r
).save
)(null))))
12509 private alias CS
= typeof(((const R
* r
) => (*r
).save
)(null));
12512 static if (isSafe
!((R
* r
) => (*r
).save
))
12514 @property RefRange
!S
save() @trusted
12519 static if (is(typeof(((const R
* r
) => (*r
).save
)(null)))) @property RefRange
!CS
save() @trusted const
12526 @property RefRange
!S
save()
12531 static if (is(typeof(((const R
* r
) => (*r
).save
)(null)))) @property RefRange
!CS
save() const
12542 auto opSlice()() const
12547 private static string
_genSave() @safe pure nothrow
12549 return `import core.lifetime : emplace;` ~
12550 `alias S = typeof((() => (*_range).save)());` ~
12551 `static assert(isForwardRange!S, S.stringof ~ " is not a forward range.");` ~
12552 `auto mem = new void[S.sizeof];` ~
12553 `emplace!S(mem, cast(S)(*_range).save);` ~
12554 `return RefRange!S(cast(S*) mem.ptr);`;
12557 static assert(isForwardRange
!RefRange
);
12564 Only defined if `isBidirectionalRange!R` is `true`.
12566 @property auto back() {assert(0);}
12568 @property auto back() const {assert(0);}
12570 @property auto back(ElementType
!R value
) {assert(0);}
12572 else static if (isBidirectionalRange
!R
)
12574 @property auto back()
12576 return (*_range
).back
;
12579 static if (is(typeof(((const R
* r
) => (*r
).back
)(null)))) @property auto back() const
12581 return (*_range
).back
;
12584 static if (is(typeof((*_range
).back
= (*_range
).back
))) @property auto back(ElementType
!R value
)
12586 return (*_range
).back
= value
;
12592 static if (isBidirectionalRange
!R
) void popBack()
12594 return (*_range
).popBack();
12601 Only defined if `isRandomAccessRange!R` is `true`.
12603 auto ref opIndex(IndexType
)(IndexType index
) {assert(0);}
12606 auto ref opIndex(IndexType
)(IndexType index
) const {assert(0);}
12608 else static if (isRandomAccessRange
!R
)
12610 auto ref opIndex(IndexType
)(IndexType index
)
12611 if (is(typeof((*_range
)[index
])))
12613 return (*_range
)[index
];
12616 auto ref opIndex(IndexType
)(IndexType index
) const
12617 if (is(typeof((*cast(const R
*)_range
)[index
])))
12619 return (*_range
)[index
];
12625 Only defined if `hasMobileElements!R` and `isForwardRange!R` are
12628 static if (hasMobileElements
!R
&& isForwardRange
!R
) auto moveFront()
12630 return (*_range
).moveFront();
12635 Only defined if `hasMobileElements!R` and `isBidirectionalRange!R`
12638 static if (hasMobileElements
!R
&& isBidirectionalRange
!R
) auto moveBack()
12640 return (*_range
).moveBack();
12645 Only defined if `hasMobileElements!R` and `isRandomAccessRange!R`
12648 static if (hasMobileElements
!R
&& isRandomAccessRange
!R
) auto moveAt(size_t index
)
12650 return (*_range
).moveAt(index
);
12656 /// Only defined if `hasLength!R` is `true`.
12657 @property size_t
length();
12659 @property size_t
length() const;
12661 alias opDollar
= length
;
12663 else static if (hasLength
!R
)
12665 @property auto length()
12667 return (*_range
).length
;
12669 static if (is(typeof(((const R
* r
) => (*r
).length
)(null)))) @property auto length() const
12671 return (*_range
).length
;
12673 alias opDollar
= length
;
12680 Only defined if `hasSlicing!R` is `true`.
12682 auto opSlice(IndexType1
, IndexType2
)
12683 (IndexType1 begin
, IndexType2 end
) {assert(0);}
12686 auto opSlice(IndexType1
, IndexType2
)
12687 (IndexType1 begin
, IndexType2 end
) const {assert(0);}
12689 else static if (hasSlicing
!R
)
12691 private alias T
= typeof((*_range
)[1 .. 2]);
12692 static if (is(typeof((*cast(const R
*)_range
)[1 .. 2])))
12694 private alias CT
= typeof((*cast(const R
*)_range
)[1 .. 2]);
12697 RefRange
!T
opSlice(IndexType1
, IndexType2
)
12698 (IndexType1 begin
, IndexType2 end
)
12699 if (is(typeof((*_range
)[begin
.. end
])))
12701 mixin(_genOpSlice());
12704 RefRange
!CT
opSlice(IndexType1
, IndexType2
)
12705 (IndexType1 begin
, IndexType2 end
) const
12706 if (is(typeof((*cast(const R
*)_range
)[begin
.. end
])))
12708 mixin(_genOpSlice());
12711 private static string
_genOpSlice() @safe pure nothrow
12713 return `import core.lifetime : emplace;` ~
12714 `alias S = typeof((*_range)[begin .. end]);` ~
12715 `static assert(hasSlicing!S, S.stringof ~ " is not sliceable.");` ~
12716 `auto mem = new void[S.sizeof];` ~
12717 `emplace!S(mem, cast(S)(*_range)[begin .. end]);` ~
12718 `return RefRange!S(cast(S*) mem.ptr);`;
12731 import std
.algorithm
.searching
: find
;
12732 ubyte[] buffer
= [1, 9, 45, 12, 22];
12733 auto found1
= find(buffer
, 45);
12734 assert(found1
== [45, 12, 22]);
12735 assert(buffer
== [1, 9, 45, 12, 22]);
12737 auto wrapped1
= refRange(&buffer
);
12738 auto found2
= find(wrapped1
, 45);
12739 assert(*found2
.ptr
== [45, 12, 22]);
12740 assert(buffer
== [45, 12, 22]);
12742 auto found3
= find(wrapped1
.save
, 22);
12743 assert(*found3
.ptr
== [22]);
12744 assert(buffer
== [45, 12, 22]);
12746 string
str = "hello world";
12747 auto wrappedStr
= refRange(&str);
12748 assert(str.front
== 'h');
12750 assert(str == " world");
12751 assert(wrappedStr
.front
== ' ');
12752 assert(*wrappedStr
.ptr
== " world");
12755 /// opAssign Example.
12758 ubyte[] buffer1
= [1, 2, 3, 4, 5];
12759 ubyte[] buffer2
= [6, 7, 8, 9, 10];
12760 auto wrapped1
= refRange(&buffer1
);
12761 auto wrapped2
= refRange(&buffer2
);
12762 assert(wrapped1
.ptr
is &buffer1
);
12763 assert(wrapped2
.ptr
is &buffer2
);
12764 assert(wrapped1
.ptr
!is wrapped2
.ptr
);
12765 assert(buffer1
!= buffer2
);
12767 wrapped1
= wrapped2
;
12769 //Everything points to the same stuff as before.
12770 assert(wrapped1
.ptr
is &buffer1
);
12771 assert(wrapped2
.ptr
is &buffer2
);
12772 assert(wrapped1
.ptr
!is wrapped2
.ptr
);
12774 //But buffer1 has changed due to the assignment.
12775 assert(buffer1
== [6, 7, 8, 9, 10]);
12776 assert(buffer2
== [6, 7, 8, 9, 10]);
12778 buffer2
= [11, 12, 13, 14, 15];
12780 //Everything points to the same stuff as before.
12781 assert(wrapped1
.ptr
is &buffer1
);
12782 assert(wrapped2
.ptr
is &buffer2
);
12783 assert(wrapped1
.ptr
!is wrapped2
.ptr
);
12785 //But buffer2 has changed due to the assignment.
12786 assert(buffer1
== [6, 7, 8, 9, 10]);
12787 assert(buffer2
== [11, 12, 13, 14, 15]);
12791 //The pointer changed for wrapped2 but not wrapped1.
12792 assert(wrapped1
.ptr
is &buffer1
);
12793 assert(wrapped2
.ptr
is null);
12794 assert(wrapped1
.ptr
!is wrapped2
.ptr
);
12796 //buffer2 is not affected by the assignment.
12797 assert(buffer1
== [6, 7, 8, 9, 10]);
12798 assert(buffer2
== [11, 12, 13, 14, 15]);
12803 import std
.algorithm
.iteration
: filter
;
12805 ubyte[] buffer
= [1, 2, 3, 4, 5];
12806 auto wrapper
= refRange(&buffer
);
12807 auto p
= wrapper
.ptr
;
12808 auto f
= wrapper
.front
;
12810 auto e
= wrapper
.empty
;
12811 wrapper
.popFront();
12812 auto s
= wrapper
.save
;
12813 auto b
= wrapper
.back
;
12816 auto i
= wrapper
[0];
12817 wrapper
.moveFront();
12818 wrapper
.moveBack();
12820 auto l
= wrapper
.length
;
12821 auto sl
= wrapper
[0 .. 1];
12822 assert(wrapper
[0 .. $].length
== buffer
[0 .. $].length
);
12826 ubyte[] buffer
= [1, 2, 3, 4, 5];
12827 const wrapper
= refRange(&buffer
);
12828 const p
= wrapper
.ptr
;
12829 const f
= wrapper
.front
;
12830 const e
= wrapper
.empty
;
12831 const s
= wrapper
.save
;
12832 const b
= wrapper
.back
;
12833 const i
= wrapper
[0];
12834 const l
= wrapper
.length
;
12835 const sl
= wrapper
[0 .. 1];
12839 ubyte[] buffer
= [1, 2, 3, 4, 5];
12840 auto filtered
= filter
!"true"(buffer
);
12841 auto wrapper
= refRange(&filtered
);
12842 auto p
= wrapper
.ptr
;
12843 auto f
= wrapper
.front
;
12845 auto e
= wrapper
.empty
;
12846 wrapper
.popFront();
12847 auto s
= wrapper
.save
;
12848 wrapper
.moveFront();
12852 ubyte[] buffer
= [1, 2, 3, 4, 5];
12853 auto filtered
= filter
!"true"(buffer
);
12854 const wrapper
= refRange(&filtered
);
12855 const p
= wrapper
.ptr
;
12857 //Cannot currently be const. filter needs to be updated to handle const.
12859 const f = wrapper.front;
12860 const e = wrapper.empty;
12861 const s = wrapper.save;
12866 string
str = "hello world";
12867 auto wrapper
= refRange(&str);
12868 auto p
= wrapper
.ptr
;
12869 auto f
= wrapper
.front
;
12870 auto e
= wrapper
.empty
;
12871 wrapper
.popFront();
12872 auto s
= wrapper
.save
;
12873 auto b
= wrapper
.back
;
12878 // https://issues.dlang.org/show_bug.cgi?id=16534
12879 // opDollar should be defined if the wrapped range defines length.
12880 auto range
= 10.iota
.takeExactly(5);
12881 auto wrapper
= refRange(&range
);
12882 assert(wrapper
.length
== 5);
12883 assert(wrapper
[0 .. $ - 1].length
== 4);
12890 ubyte[] buffer1
= [1, 2, 3, 4, 5];
12891 ubyte[] buffer2
= [6, 7, 8, 9, 10];
12892 RefRange
!(ubyte[]) wrapper1
;
12893 RefRange
!(ubyte[]) wrapper2
= refRange(&buffer2
);
12894 assert(wrapper1
.ptr
is null);
12895 assert(wrapper2
.ptr
is &buffer2
);
12897 wrapper1
= refRange(&buffer1
);
12898 assert(wrapper1
.ptr
is &buffer1
);
12900 wrapper1
= wrapper2
;
12901 assert(wrapper1
.ptr
is &buffer1
);
12902 assert(buffer1
== buffer2
);
12904 wrapper1
= RefRange
!(ubyte[]).init
;
12905 assert(wrapper1
.ptr
is null);
12906 assert(wrapper2
.ptr
is &buffer2
);
12907 assert(buffer1
== buffer2
);
12908 assert(buffer1
== [6, 7, 8, 9, 10]);
12911 assert(wrapper2
.ptr
is null);
12912 assert(buffer2
== [6, 7, 8, 9, 10]);
12917 import std
.algorithm
.comparison
: equal
;
12918 import std
.algorithm
.mutation
: bringToFront
;
12919 import std
.algorithm
.searching
: commonPrefix
, find
, until
;
12920 import std
.algorithm
.sorting
: sort
;
12922 //Test that ranges are properly consumed.
12924 int[] arr
= [1, 42, 2, 41, 3, 40, 4, 42, 9];
12925 auto wrapper
= refRange(&arr
);
12927 assert(*find(wrapper
, 41).ptr
== [41, 3, 40, 4, 42, 9]);
12928 assert(arr
== [41, 3, 40, 4, 42, 9]);
12930 assert(*drop(wrapper
, 2).ptr
== [40, 4, 42, 9]);
12931 assert(arr
== [40, 4, 42, 9]);
12933 assert(equal(until(wrapper
, 42), [40, 4]));
12934 assert(arr
== [42, 9]);
12936 assert(find(wrapper
, 12).empty
);
12941 string
str = "Hello, world-like object.";
12942 auto wrapper
= refRange(&str);
12944 assert(*find(wrapper
, "l").ptr
== "llo, world-like object.");
12945 assert(str == "llo, world-like object.");
12947 assert(equal(take(wrapper
, 5), "llo, "));
12948 assert(str == "world-like object.");
12951 //Test that operating on saved ranges does not consume the original.
12953 int[] arr
= [1, 42, 2, 41, 3, 40, 4, 42, 9];
12954 auto wrapper
= refRange(&arr
);
12955 auto saved
= wrapper
.save
;
12956 saved
.popFrontN(3);
12957 assert(*saved
.ptr
== [41, 3, 40, 4, 42, 9]);
12958 assert(arr
== [1, 42, 2, 41, 3, 40, 4, 42, 9]);
12962 string
str = "Hello, world-like object.";
12963 auto wrapper
= refRange(&str);
12964 auto saved
= wrapper
.save
;
12965 saved
.popFrontN(13);
12966 assert(*saved
.ptr
== "like object.");
12967 assert(str == "Hello, world-like object.");
12970 //Test that functions which use save work properly.
12972 int[] arr
= [1, 42];
12973 auto wrapper
= refRange(&arr
);
12974 assert(equal(commonPrefix(wrapper
, [1, 27]), [1]));
12978 int[] arr
= [4, 5, 6, 7, 1, 2, 3];
12979 auto wrapper
= refRange(&arr
);
12980 assert(bringToFront(wrapper
[0 .. 4], wrapper
[4 .. arr
.length
]) == 3);
12981 assert(arr
== [1, 2, 3, 4, 5, 6, 7]);
12984 //Test bidirectional functions.
12986 int[] arr
= [1, 42, 2, 41, 3, 40, 4, 42, 9];
12987 auto wrapper
= refRange(&arr
);
12989 assert(wrapper
.back
== 9);
12990 assert(arr
== [1, 42, 2, 41, 3, 40, 4, 42, 9]);
12993 assert(arr
== [1, 42, 2, 41, 3, 40, 4, 42]);
12997 string
str = "Hello, world-like object.";
12998 auto wrapper
= refRange(&str);
13000 assert(wrapper
.back
== '.');
13001 assert(str == "Hello, world-like object.");
13004 assert(str == "Hello, world-like object");
13007 //Test random access functions.
13009 int[] arr
= [1, 42, 2, 41, 3, 40, 4, 42, 9];
13010 auto wrapper
= refRange(&arr
);
13012 assert(wrapper
[2] == 2);
13013 assert(arr
== [1, 42, 2, 41, 3, 40, 4, 42, 9]);
13015 assert(*wrapper
[3 .. 6].ptr
!= null, [41, 3, 40]);
13016 assert(arr
== [1, 42, 2, 41, 3, 40, 4, 42, 9]);
13019 //Test move functions.
13021 int[] arr
= [1, 42, 2, 41, 3, 40, 4, 42, 9];
13022 auto wrapper
= refRange(&arr
);
13024 auto t1
= wrapper
.moveFront();
13025 auto t2
= wrapper
.moveBack();
13026 wrapper
.front
= t2
;
13028 assert(arr
== [9, 42, 2, 41, 3, 40, 4, 42, 1]);
13030 sort(wrapper
.save
);
13031 assert(arr
== [1, 2, 3, 4, 9, 40, 41, 42, 42]);
13039 @property int front() @safe const pure nothrow { return 0; }
13040 enum bool empty
= false;
13041 void popFront() @safe pure nothrow { }
13042 @property auto save() @safe pure nothrow return scope { return this; }
13046 auto wrapper
= refRange(&s
);
13047 static assert(isInfinite
!(typeof(wrapper
)));
13054 @property int front() @safe const pure nothrow { return 0; }
13055 @property bool empty() @safe const pure nothrow { return false; }
13056 void popFront() @safe pure nothrow { }
13057 @property auto save() @safe pure nothrow return scope { return this; }
13059 static assert(isForwardRange
!C
);
13062 auto cWrapper
= refRange(&c
);
13063 static assert(is(typeof(cWrapper
) == C
));
13064 assert(cWrapper
is c
);
13067 // https://issues.dlang.org/show_bug.cgi?id=14373
13072 @property int front() {return 0;}
13073 void popFront() {empty
= true;}
13074 bool empty
= false;
13077 refRange(&r
).popFront();
13081 // https://issues.dlang.org/show_bug.cgi?id=14575
13087 alias back
= front
;
13088 bool empty
= false;
13089 void popFront() {empty
= true;}
13090 alias popBack
= popFront
;
13091 @property R
save() {return this;}
13093 static assert(isBidirectionalRange
!R
);
13095 auto rr
= refRange(&r
);
13099 @property Object
front() {return null;}
13100 @property const(Object
) front() const {return null;}
13101 alias back
= front
;
13102 bool empty
= false;
13103 void popFront() {empty
= true;}
13104 alias popBack
= popFront
;
13105 @property R2
save() {return this;}
13107 static assert(isBidirectionalRange
!R2
);
13109 auto rr2
= refRange(&r2
);
13112 // https://issues.dlang.org/show_bug.cgi?id=24801
13121 bool empty() => false;
13124 auto r
= RefRange
!R(&range
);
13131 size_t
length() => end
- start
;
13132 int opIndex(size_t i
) => 0;
13135 int front() => this[0];
13136 int back() => this[length
-1];
13137 void popFront() { start
++; }
13138 void popBack() { end
--; }
13139 bool empty() => length
== 0;
13140 R
save() const => R();
13144 auto r
= RefRange
!R(&range
);
13151 auto refRange(R
)(R
* range
)
13152 if (isInputRange
!R
)
13154 static if (!is(R
== class))
13155 return RefRange
!R(range
);
13160 // https://issues.dlang.org/show_bug.cgi?id=9060
13163 import std
.algorithm
.iteration
: map
, joiner
, group
;
13164 import std
.algorithm
.searching
: until
;
13165 // fix for std.algorithm
13166 auto r
= map
!(x
=> 0)([1]);
13173 @property empty() { return input
.empty
; }
13174 @property front() { return input
.front
; }
13175 void popFront() { input
.popFront(); }
13176 @property save() { return NRAR(input
.save
); }
13179 cycle(n1
); // non random access range version
13183 // fix for std.range
13188 @property empty() { return true; }
13189 @property front() { return input
; }
13190 void popFront() { }
13191 @property save() { return NRAR2(input
.save
); }
13193 auto n2
= NRAR2(n1
);
13199 static void foo(R
)(R r
) { until
!(x
=> x
> 7)(r
); }
13203 private struct Bitwise(R
)
13204 if (isInputRange
!R
&& isIntegral
!(ElementType
!R
))
13206 import std
.traits
: Unsigned
;
13208 alias ElemType
= ElementType
!R
;
13209 alias UnsignedElemType
= Unsigned
!ElemType
;
13212 enum bitsNum
= ElemType
.sizeof
* 8;
13213 size_t maskPos
= 1;
13215 static if (isBidirectionalRange
!R
)
13217 size_t backMaskPos
= bitsNum
;
13221 this()(auto ref R range
)
13226 static if (isInfinite
!R
)
13228 enum empty
= false;
13233 * Check if the range is empty
13235 * Returns: a boolean true or false
13239 static if (hasLength
!R
)
13241 return length
== 0;
13243 else static if (isBidirectionalRange
!R
)
13252 If we have consumed the last element of the range both from
13253 the front and the back, then the masks positions will overlap
13255 return parent
.save
.dropOne
.empty
&& (maskPos
> backMaskPos
);
13261 If we consumed the last element of the range, but not all the
13262 bits in the last element
13264 return parent
.empty
;
13272 return (parent
.front
& mask(maskPos
)) != 0;
13279 if (maskPos
> bitsNum
)
13286 static if (hasLength
!R
)
13290 auto len
= parent
.length
* bitsNum
- (maskPos
- 1);
13291 static if (isBidirectionalRange
!R
)
13293 len
-= bitsNum
- backMaskPos
;
13298 alias opDollar
= length
;
13301 static if (isForwardRange
!R
)
13303 typeof(this) save()
13305 auto result
= this;
13306 result
.parent
= parent
.save
;
13311 static if (isBidirectionalRange
!R
)
13316 return (parent
.back
& mask(backMaskPos
)) != 0;
13323 if (backMaskPos
== 0)
13326 backMaskPos
= bitsNum
;
13331 static if (isRandomAccessRange
!R
)
13334 Return the `n`th bit within the range
13336 bool opIndex(size_t n
)
13340 If it does not have the length property, it means that R is
13343 static if (hasLength
!R
)
13345 assert(n
< length
, "Index out of bounds");
13350 immutable size_t remainingBits
= bitsNum
- maskPos
+ 1;
13351 // If n >= maskPos, then the bit sign will be 1, otherwise 0
13352 immutable ptrdiff_t sign
= (remainingBits
- n
- 1) >> (ptrdiff_t
.sizeof
* 8 - 1);
13354 By truncating n with remainingBits bits we have skipped the
13355 remaining bits in parent[0], so we need to add 1 to elemIndex.
13357 Because bitsNum is a power of 2, n / bitsNum == n >> bitsNum.bsf
13359 import core
.bitop
: bsf;
13360 immutable size_t elemIndex
= sign
* (((n
- remainingBits
) >> bitsNum
.bsf) + 1);
13363 Since the indexing is from LSB to MSB, we need to index at the
13364 remainder of (n - remainingBits).
13366 Because bitsNum is a power of 2, n % bitsNum == n & (bitsNum - 1)
13368 immutable size_t elemMaskPos
= (sign ^
1) * (maskPos
+ n
)
13369 + sign
* (1 + ((n
- remainingBits
) & (bitsNum
- 1)));
13371 return (parent
[elemIndex
] & mask(elemMaskPos
)) != 0;
13374 static if (hasAssignableElements
!R
)
13377 Assigns `flag` to the `n`th bit within the range
13379 void opIndexAssign(bool flag
, size_t n
)
13382 static if (hasLength
!R
)
13384 assert(n
< length
, "Index out of bounds");
13389 import core
.bitop
: bsf;
13391 immutable size_t remainingBits
= bitsNum
- maskPos
+ 1;
13392 immutable ptrdiff_t sign
= (remainingBits
- n
- 1) >> (ptrdiff_t
.sizeof
* 8 - 1);
13393 immutable size_t elemIndex
= sign
* (((n
- remainingBits
) >> bitsNum
.bsf) + 1);
13394 immutable size_t elemMaskPos
= (sign ^
1) * (maskPos
+ n
)
13395 + sign
* (1 + ((n
- remainingBits
) & (bitsNum
- 1)));
13397 auto elem
= parent
[elemIndex
];
13398 auto elemMask
= mask(elemMaskPos
);
13399 parent
[elemIndex
] = cast(UnsignedElemType
)(flag
* (elem | elemMask
)
13400 + (flag ^
1) * (elem
& ~elemMask
));
13404 Bitwise
!R
opSlice()
13409 Bitwise
!R
opSlice(size_t start
, size_t end
)
13412 assert(start
< end
, "Invalid bounds: end <= start");
13416 import core
.bitop
: bsf;
13418 size_t remainingBits
= bitsNum
- maskPos
+ 1;
13419 ptrdiff_t sign
= (remainingBits
- start
- 1) >> (ptrdiff_t
.sizeof
* 8 - 1);
13420 immutable size_t startElemIndex
= sign
* (((start
- remainingBits
) >> bitsNum
.bsf) + 1);
13421 immutable size_t startElemMaskPos
= (sign ^
1) * (maskPos
+ start
)
13422 + sign
* (1 + ((start
- remainingBits
) & (bitsNum
- 1)));
13424 immutable size_t sliceLen
= end
- start
- 1;
13425 remainingBits
= bitsNum
- startElemMaskPos
+ 1;
13426 sign
= (remainingBits
- sliceLen
- 1) >> (ptrdiff_t
.sizeof
* 8 - 1);
13427 immutable size_t endElemIndex
= startElemIndex
13428 + sign
* (((sliceLen
- remainingBits
) >> bitsNum
.bsf) + 1);
13429 immutable size_t endElemMaskPos
= (sign ^
1) * (startElemMaskPos
+ sliceLen
)
13430 + sign
* (1 + ((sliceLen
- remainingBits
) & (bitsNum
- 1)));
13432 typeof(return) result
;
13433 // Get the slice to be returned from the parent
13434 result
.parent
= (parent
[startElemIndex
.. endElemIndex
+ 1]).save
;
13435 result
.maskPos
= startElemMaskPos
;
13436 static if (isBidirectionalRange
!R
)
13438 result
.backMaskPos
= endElemMaskPos
;
13445 auto mask(size_t maskPos
)
13447 return (1UL << (maskPos
- 1UL));
13452 Bitwise adapter over an integral type range. Consumes the range elements bit by
13453 bit, from the least significant bit to the most significant bit.
13456 R = an integral $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to iterate over
13457 range = range to consume bit by by
13460 A `Bitwise` input range with propagated forward, bidirectional
13461 and random access capabilities
13463 auto bitwise(R
)(auto ref R range
)
13464 if (isInputRange
!R
&& isIntegral
!(ElementType
!R
))
13466 return Bitwise
!R(range
);
13470 @safe pure unittest
13472 import std
.algorithm
.comparison
: equal
;
13473 import std
.format
: format
;
13475 // 00000011 00001001
13476 ubyte[] arr
= [3, 9];
13477 auto r
= arr
.bitwise
;
13479 // iterate through it as with any other range
13480 assert(format("%(%d%)", r
) == "1100000010010000");
13481 assert(format("%(%d%)", r
.retro
).equal("1100000010010000".retro
));
13483 auto r2
= r
[5 .. $];
13486 assert(arr
[0] == 7);
13487 assert(r
[5] == r2
[0]);
13490 /// You can use bitwise to implement an uniform bool generator
13493 import std
.algorithm
.comparison
: equal
;
13494 import std
.random
: rndGen
;
13496 auto rb
= rndGen
.bitwise
;
13497 static assert(isInfinite
!(typeof(rb
)));
13499 auto rb2
= rndGen
.bitwise
;
13500 // Don't forget that structs are passed by value
13501 assert(rb
.take(10).equal(rb2
.take(10)));
13504 // Test nogc inference
13505 @safe @nogc unittest
13507 static ubyte[] arr
= [3, 9];
13508 auto bw
= arr
.bitwise
;
13510 auto bw3
= bw
[8 .. $];
13513 assert(arr
[1] == 13);
13515 assert(bw
[$ - 6] == bw2
[$ - 6]);
13516 assert(bw
[$ - 6] == bw3
[$ - 6]);
13519 // Test all range types over all integral types
13520 @safe pure nothrow unittest
13522 import std
.meta
: AliasSeq
;
13523 import std
.internal
.test.dummyrange
;
13525 alias IntegralTypes
= AliasSeq
!(byte, ubyte, short, ushort, int, uint,
13527 foreach (IntegralType
; IntegralTypes
)
13529 foreach (T
; AllDummyRangesType
!(IntegralType
[]))
13532 auto bw
= Bitwise
!T(a
);
13534 static if (isForwardRange
!T
)
13536 auto bwFwdSave
= bw
.save
;
13539 static if (isBidirectionalRange
!T
)
13541 auto bwBack
= bw
.save
;
13542 auto bwBackSave
= bw
.save
;
13545 static if (hasLength
!T
)
13547 auto bwLength
= bw
.length
;
13548 assert(bw
.length
== (IntegralType
.sizeof
* 8 * a
.length
));
13549 static if (isForwardRange
!T
)
13551 assert(bw
.length
== bwFwdSave
.length
);
13555 // Make sure front and back are not the mechanisms that modify the range
13556 long numCalls
= 42;
13557 bool initialFrontValue
;
13561 initialFrontValue
= bw
.front
;
13564 while (!bw
.empty
&& (--numCalls
))
13567 assert(bw
.front
== initialFrontValue
);
13571 Check that empty works properly and that popFront does not get called
13572 more times than it should
13579 static if (hasLength
!T
)
13581 assert(bw
.length
== bwLength
);
13585 static if (isForwardRange
!T
)
13587 assert(bw
.front
== bwFwdSave
.front
);
13588 bwFwdSave
.popFront();
13591 static if (isBidirectionalRange
!T
)
13593 assert(bwBack
.front
== bwBackSave
.front
);
13595 bwBackSave
.popBack();
13600 auto rangeLen
= numCalls
/ (IntegralType
.sizeof
* 8);
13601 assert(numCalls
== (IntegralType
.sizeof
* 8 * rangeLen
));
13603 static if (isForwardRange
!T
)
13605 assert(bwFwdSave
.empty
);
13608 static if (isBidirectionalRange
!T
)
13610 assert(bwBack
.empty
);
13616 // Test opIndex and opSlice
13619 import std
.meta
: AliasSeq
;
13620 alias IntegralTypes
= AliasSeq
!(byte, ubyte, short, ushort, int, uint,
13622 foreach (IntegralType
; IntegralTypes
)
13624 size_t bitsNum
= IntegralType
.sizeof
* 8;
13626 auto first
= IntegralType(1);
13628 // 2 ^ (bitsNum - 1)
13629 auto second
= cast(IntegralType
)(IntegralType(1) << (bitsNum
- 2));
13631 IntegralType
[] a
= [first
, second
];
13632 auto bw
= Bitwise
!(IntegralType
[])(a
);
13634 // Check against lsb of a[0]
13635 assert(bw
[0] == true);
13636 // Check against msb - 1 of a[1]
13637 assert(bw
[2 * bitsNum
- 2] == true);
13640 assert(bw
[2 * bitsNum
- 3] == true);
13642 import std
.exception
: assertThrown
;
13644 version (D_NoBoundsChecks
) {}
13647 // Check out of bounds error
13648 assertThrown
!Error(bw
[2 * bitsNum
- 1]);
13652 assert(bw
[2] == true);
13654 assert(bw
[1] == true);
13656 auto bw2
= bw
[0 .. $ - 5];
13658 assert(bw2
.length
== bw
.length
- 5);
13659 assert(bw2
.length
== bw3
.length
);
13661 assert(bw2
.length
!= bw3
.length
);
13665 /*********************************
13666 * An OutputRange that discards the data it receives.
13670 void put(E
)(scope const E
) pure @safe @nogc nothrow {}
13674 auto ref nullSink()
13676 static NullSink sink
;
13681 @safe nothrow unittest
13683 import std
.algorithm
.iteration
: map
;
13684 import std
.algorithm
.mutation
: copy
;
13685 [4, 5, 6].map
!(x
=> x
* 2).copy(nullSink
); // data is discarded
13691 import std
.csv
: csvNextToken
;
13693 string line
= "a,b,c";
13695 // ignore the first column
13696 line
.csvNextToken(nullSink
, ',', '"');
13699 // look at the second column
13700 Appender
!string app
;
13701 line
.csvNextToken(app
, ',', '"');
13702 assert(app
.data
== "b");
13711 assert(r
.front
== 1);
13716 Implements a "tee" style pipe, wrapping an input range so that elements of the
13717 range can be passed to a provided function or $(LREF OutputRange) as they are
13718 iterated over. This is useful for printing out intermediate values in a long
13719 chain of range code, performing some operation with side-effects on each call
13720 to `front` or `popFront`, or diverting the elements of a range into an
13721 auxiliary $(LREF OutputRange).
13723 It is important to note that as the resultant range is evaluated lazily,
13724 in the case of the version of `tee` that takes a function, the function
13725 will not actually be executed until the range is "walked" using functions
13726 that evaluate ranges, such as $(REF array, std,array) or
13727 $(REF fold, std,algorithm,iteration).
13730 pipeOnPop = If `Yes.pipeOnPop`, simply iterating the range without ever
13731 calling `front` is enough to have `tee` mirror elements to `outputRange` (or,
13732 respectively, `fun`). Note that each `popFront()` call will mirror the
13733 old `front` value, not the new one. This means that the last value will
13734 not be forwarded if the range isn't iterated until empty. If
13735 `No.pipeOnPop`, only elements for which `front` does get called will be
13736 also sent to `outputRange`/`fun`. If `front` is called twice for the same
13737 element, it will still be sent only once. If this caching is undesired,
13738 consider using $(REF map, std,algorithm,iteration) instead.
13739 inputRange = The input range being passed through.
13740 outputRange = This range will receive elements of `inputRange` progressively
13741 as iteration proceeds.
13742 fun = This function will be called with elements of `inputRange`
13743 progressively as iteration proceeds.
13746 An input range that offers the elements of `inputRange`. Regardless of
13747 whether `inputRange` is a more powerful range (forward, bidirectional etc),
13748 the result is always an input range. Reading this causes `inputRange` to be
13749 iterated and returns its elements in turn. In addition, the same elements
13750 will be passed to `outputRange` or `fun` as well.
13752 See_Also: $(REF each, std,algorithm,iteration)
13754 auto tee(Flag
!"pipeOnPop" pipeOnPop
= Yes
.pipeOnPop
, R1
, R2
)(R1 inputRange
, R2 outputRange
)
13755 if (isInputRange
!R1
&& isOutputRange
!(R2
, ElementType
!R1
))
13757 static struct Result
13760 private R2 _output
;
13761 static if (!pipeOnPop
)
13763 private bool _frontAccessed
;
13766 mixin ImplementLength
!_input
;
13768 static if (isInfinite
!R1
)
13770 enum bool empty
= false;
13774 @property bool empty() { return _input
.empty
; }
13779 assert(!_input
.empty
, "Attempting to popFront an empty tee");
13780 static if (pipeOnPop
)
13782 put(_output
, _input
.front
);
13786 _frontAccessed
= false;
13791 @property auto ref front()
13793 assert(!_input
.empty
, "Attempting to fetch the front of an empty tee");
13794 static if (!pipeOnPop
)
13796 if (!_frontAccessed
)
13798 _frontAccessed
= true;
13799 put(_output
, _input
.front
);
13802 return _input
.front
;
13806 return Result(inputRange
, outputRange
);
13810 auto tee(alias fun
, Flag
!"pipeOnPop" pipeOnPop
= Yes
.pipeOnPop
, R1
)(R1 inputRange
)
13811 if (is(typeof(fun
) == void) || isSomeFunction
!fun
)
13813 import std
.traits
: isDelegate
, isFunctionPointer
;
13815 Distinguish between function literals and template lambdas
13816 when using either as an $(LREF OutputRange). Since a template
13817 has no type, typeof(template) will always return void.
13818 If it's a template lambda, it's first necessary to instantiate
13819 it with `ElementType!R1`.
13821 static if (is(typeof(fun
) == void))
13822 alias _fun
= fun
!(ElementType
!R1
);
13826 static if (isFunctionPointer
!_fun || isDelegate
!_fun
)
13828 return tee
!pipeOnPop(inputRange
, _fun
);
13832 return tee
!pipeOnPop(inputRange
, &_fun
);
13839 import std
.algorithm
.comparison
: equal
;
13840 import std
.algorithm
.iteration
: filter
, map
;
13842 // Sum values while copying
13843 int[] values
= [1, 4, 9, 16, 25];
13845 auto newValues
= values
.tee
!(a
=> sum
+= a
).array
;
13846 assert(equal(newValues
, values
));
13847 assert(sum
== 1 + 4 + 9 + 16 + 25);
13849 // Count values that pass the first filter
13851 auto newValues4
= values
.filter
!(a
=> a
< 10)
13852 .tee
!(a
=> count
++)
13854 .filter
!(a
=> a
< 10);
13856 //Fine, equal also evaluates any lazy ranges passed to it.
13857 //count is not 3 until equal evaluates newValues4
13858 assert(equal(newValues4
, [2, 5]));
13859 assert(count
== 3);
13865 import std
.algorithm
.comparison
: equal
;
13866 import std
.algorithm
.iteration
: filter
, map
;
13868 int[] values
= [1, 4, 9, 16, 25];
13871 auto newValues
= values
.filter
!(a
=> a
< 10)
13872 .tee
!(a
=> count
++, No
.pipeOnPop
)
13874 .filter
!(a
=> a
< 10);
13876 auto val
= newValues
.front
;
13877 assert(count
== 1);
13878 //front is only evaluated once per element
13879 val
= newValues
.front
;
13880 assert(count
== 1);
13882 //popFront() called, fun will be called
13883 //again on the next access to front
13884 newValues
.popFront();
13886 assert(count
== 2);
13888 int[] preMap
= new int[](3), postMap
= [];
13889 auto mappedValues
= values
.filter
!(a
=> a
< 10)
13890 //Note the two different ways of using tee
13893 .tee
!(a
=> postMap
~= a
)
13894 .filter
!(a
=> a
< 10);
13895 assert(equal(mappedValues
, [2, 5]));
13896 assert(equal(preMap
, [1, 4, 9]));
13897 assert(equal(postMap
, [2, 5, 10]));
13903 import std
.algorithm
.comparison
: equal
;
13904 import std
.algorithm
.iteration
: filter
, map
;
13906 char[] txt
= "Line one, Line 2".dup
;
13908 bool isVowel(dchar c
)
13910 import std
.string
: indexOf
;
13911 return "AaEeIiOoUu".indexOf(c
) != -1;
13914 int vowelCount
= 0;
13915 int shiftedCount
= 0;
13916 auto removeVowels
= txt
.tee
!(c
=> isVowel(c
) ? vowelCount
++ : 0)
13917 .filter
!(c
=> !isVowel(c
))
13918 .map
!(c
=> (c
== ' ') ? c
: c
+ 1)
13919 .tee
!(c
=> isVowel(c
) ? shiftedCount
++ : 0);
13920 assert(equal(removeVowels
, "Mo o- Mo 3"));
13921 assert(vowelCount
== 6);
13922 assert(shiftedCount
== 3);
13927 // Manually stride to test different pipe behavior.
13928 void testRange(Range
)(Range r
)
13930 const int strideLen
= 3;
13932 ElementType
!Range elem1
;
13933 ElementType
!Range elem2
;
13936 if (i
% strideLen
== 0)
13938 //Make sure front is only
13939 //evaluated once per item
13942 assert(elem1
== elem2
);
13949 string txt
= "abcdefghijklmnopqrstuvwxyz";
13952 auto pipeOnPop
= txt
.tee
!(a
=> popCount
++);
13953 testRange(pipeOnPop
);
13954 assert(popCount
== 26);
13956 int frontCount
= 0;
13957 auto pipeOnFront
= txt
.tee
!(a
=> frontCount
++, No
.pipeOnPop
);
13958 testRange(pipeOnFront
);
13959 assert(frontCount
== 9);
13964 import std
.algorithm
.comparison
: equal
;
13965 import std
.meta
: AliasSeq
;
13967 //Test diverting elements to an OutputRange
13968 string txt
= "abcdefghijklmnopqrstuvwxyz";
13970 dchar[] asink1
= [];
13971 auto fsink
= (dchar c
) { asink1
~= c
; };
13972 auto result1
= txt
.tee(fsink
).array
;
13973 assert(equal(txt
, result1
) && (equal(result1
, asink1
)));
13975 dchar[] _asink1
= [];
13976 auto _result1
= txt
.tee
!((dchar c
) { _asink1
~= c
; })().array
;
13977 assert(equal(txt
, _result1
) && (equal(_result1
, _asink1
)));
13979 dchar[] asink2
= new dchar[](txt
.length
);
13980 void fsink2(dchar c
) { static int i
= 0; asink2
[i
] = c
; i
++; }
13981 auto result2
= txt
.tee(&fsink2
).array
;
13982 assert(equal(txt
, result2
) && equal(result2
, asink2
));
13984 dchar[] asink3
= new dchar[](txt
.length
);
13985 auto result3
= txt
.tee(asink3
).array
;
13986 assert(equal(txt
, result3
) && equal(result3
, asink3
));
13988 static foreach (CharType
; AliasSeq
!(char, wchar, dchar))
13990 auto appSink
= appender
!(CharType
[])();
13991 auto appResult
= txt
.tee(appSink
).array
;
13992 assert(equal(txt
, appResult
) && equal(appResult
, appSink
.data
));
13995 static foreach (StringType
; AliasSeq
!(string
, wstring
, dstring
))
13997 auto appSink
= appender
!StringType();
13998 auto appResult
= txt
.tee(appSink
).array
;
13999 assert(equal(txt
, appResult
) && equal(appResult
, appSink
.data
));
14003 // https://issues.dlang.org/show_bug.cgi?id=13483
14006 static void func1(T
)(T x
) {}
14007 void func2(int x
) {}
14009 auto r
= [1, 2, 3, 4].tee
!func1
.tee
!func2
;
14013 Extends the length of the input range `r` by padding out the start of the
14014 range with the element `e`. The element `e` must be of a common type with
14015 the element type of the range `r` as defined by $(REF CommonType, std, traits).
14016 If `n` is less than the length of of `r`, then `r` is returned unmodified.
14018 If `r` is a string with Unicode characters in it, `padLeft` follows D's rules
14019 about length for strings, which is not the number of characters, or
14020 graphemes, but instead the number of encoding units. If you want to treat each
14021 grapheme as only one encoding unit long, then call
14022 $(REF byGrapheme, std, uni) before calling this function.
14024 If `r` has a length, then this is $(BIGOH 1). Otherwise, it's $(BIGOH r.length).
14027 r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with a length, or a forward range
14028 e = element to pad the range with
14029 n = the length to pad to
14032 A range containing the elements of the original range with the extra padding
14035 $(REF leftJustifier, std, string)
14037 auto padLeft(R
, E
)(R r
, E e
, size_t n
)
14039 ((isInputRange
!R
&& hasLength
!R
) || isForwardRange
!R
) &&
14040 !is(CommonType
!(ElementType
!R
, E
) == void)
14043 static if (hasLength
!R
)
14044 auto dataLength
= r
.length
;
14046 auto dataLength
= r
.save
.walkLength(n
);
14048 return e
.repeat(n
> dataLength ? n
- dataLength
: 0).chain(r
);
14052 @safe pure unittest
14054 import std
.algorithm
.comparison
: equal
;
14056 assert([1, 2, 3, 4].padLeft(0, 6).equal([0, 0, 1, 2, 3, 4]));
14057 assert([1, 2, 3, 4].padLeft(0, 3).equal([1, 2, 3, 4]));
14059 assert("abc".padLeft('_', 6).equal("___abc"));
14062 @safe pure nothrow unittest
14064 import std
.algorithm
.comparison
: equal
;
14065 import std
.internal
.test.dummyrange
: DummyRange
, Length
, RangeType
, ReturnBy
;
14066 import std
.meta
: AliasSeq
;
14068 alias DummyRanges
= AliasSeq
!(
14069 DummyRange
!(ReturnBy
.Reference
, Length
.Yes
, RangeType
.Input
),
14070 DummyRange
!(ReturnBy
.Reference
, Length
.Yes
, RangeType
.Forward
),
14071 DummyRange
!(ReturnBy
.Reference
, Length
.Yes
, RangeType
.Bidirectional
),
14072 DummyRange
!(ReturnBy
.Reference
, Length
.Yes
, RangeType
.Random
),
14073 DummyRange
!(ReturnBy
.Reference
, Length
.No
, RangeType
.Forward
),
14074 DummyRange
!(ReturnBy
.Value
, Length
.Yes
, RangeType
.Input
),
14075 DummyRange
!(ReturnBy
.Value
, Length
.Yes
, RangeType
.Forward
),
14076 DummyRange
!(ReturnBy
.Value
, Length
.Yes
, RangeType
.Bidirectional
),
14077 DummyRange
!(ReturnBy
.Value
, Length
.Yes
, RangeType
.Random
),
14078 DummyRange
!(ReturnBy
.Value
, Length
.No
, RangeType
.Forward
)
14081 foreach (Range
; DummyRanges
)
14086 .equal([0, 0, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U])
14091 // Test nogc inference
14092 @safe @nogc pure unittest
14094 import std
.algorithm
.comparison
: equal
;
14096 static immutable r1
= [1, 2, 3, 4];
14097 static immutable r2
= [0, 0, 1, 2, 3, 4];
14098 assert(r1
.padLeft(0, 6).equal(r2
));
14102 Extend the length of the input range `r` by padding out the end of the range
14103 with the element `e`. The element `e` must be of a common type with the
14104 element type of the range `r` as defined by $(REF CommonType, std, traits).
14105 If `n` is less than the length of of `r`, then the contents of `r` are
14108 The range primitives that the resulting range provides depends whether or not `r`
14109 provides them. Except the functions `back` and `popBack`, which also require
14110 the range to have a length as well as `back` and `popBack`
14113 r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with a length
14114 e = element to pad the range with
14115 n = the length to pad to
14118 A range containing the elements of the original range with the extra padding
14121 $(REF rightJustifier, std, string)
14123 auto padRight(R
, E
)(R r
, E e
, size_t n
)
14127 !is(CommonType
!(ElementType
!R
, E
) == void))
14129 static struct Result
14134 static if (hasLength
!R
)
14145 bool empty() @property
14147 static if (hasLength
!R
)
14149 return data
.empty
&& padLength
== 0;
14153 return data
.empty
&& consumed
>= minLength
;
14157 auto front() @property
14159 assert(!empty
, "Attempting to fetch the front of an empty padRight");
14160 return data
.empty ? element
: data
.front
;
14165 assert(!empty
, "Attempting to popFront an empty padRight");
14167 static if (hasLength
!R
)
14188 static if (hasLength
!R
)
14190 size_t
length() @property
14192 return data
.length
+ padLength
;
14196 static if (isForwardRange
!R
)
14198 auto save() @property
14200 typeof(this) result
= this;
14206 static if (isBidirectionalRange
!R
&& hasLength
!R
)
14208 auto back() @property
14210 assert(!empty
, "Attempting to fetch the back of an empty padRight");
14211 return padLength
> 0 ? element
: data
.back
;
14216 assert(!empty
, "Attempting to popBack an empty padRight");
14228 static if (isRandomAccessRange
!R
&& hasLength
!R
)
14230 E
opIndex(size_t index
)
14232 assert(index
<= this.length
, "Index out of bounds");
14233 return index
>= data
.length ? element
: data
[index
];
14237 static if (hasSlicing
!R
&& hasLength
!R
)
14239 auto opSlice(size_t a
, size_t b
)
14243 "Attempting to slice a padRight with a larger first argument than the second."
14247 "Attempting to slice using an out of bounds index on a padRight"
14250 a
>= data
.length ? data
[0 .. 0] : b
<= data
.length ? data
[a
.. b
] : data
[a
.. data
.length
],
14254 alias opDollar
= length
;
14257 this(R r
, E e
, size_t n
)
14261 static if (hasLength
!R
)
14263 padLength
= n
> data
.length ? n
- data
.length
: 0;
14274 return Result(r
, e
, n
);
14278 @safe pure unittest
14280 import std
.algorithm
.comparison
: equal
;
14282 assert([1, 2, 3, 4].padRight(0, 6).equal([1, 2, 3, 4, 0, 0]));
14283 assert([1, 2, 3, 4].padRight(0, 4).equal([1, 2, 3, 4]));
14285 assert("abc".padRight('_', 6).equal("abc___"));
14288 pure @safe unittest
14290 import std
.algorithm
.comparison
: equal
;
14291 import std
.internal
.test.dummyrange
: AllDummyRanges
, ReferenceInputRange
;
14292 import std
.meta
: AliasSeq
;
14294 auto string_input_range
= new ReferenceInputRange
!dchar(['a', 'b', 'c']);
14295 dchar padding
= '_';
14296 assert(string_input_range
.padRight(padding
, 6).equal("abc___"));
14298 foreach (RangeType
; AllDummyRanges
)
14303 .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0, 0])
14306 // test if Result properly uses random access ranges
14307 static if (isRandomAccessRange
!RangeType
)
14310 assert(r3
.padRight(0, 12)[0] == 1);
14311 assert(r3
.padRight(0, 12)[2] == 3);
14312 assert(r3
.padRight(0, 12)[9] == 10);
14313 assert(r3
.padRight(0, 12)[10] == 0);
14314 assert(r3
.padRight(0, 12)[11] == 0);
14317 // test if Result properly uses slicing and opDollar
14318 static if (hasSlicing
!RangeType
)
14322 .padRight(0, 12)[0 .. 3]
14326 .padRight(0, 12)[0 .. 10]
14327 .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U])
14330 .padRight(0, 12)[0 .. 11]
14331 .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0])
14334 .padRight(0, 12)[2 .. $]
14335 .equal([3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0, 0])
14338 .padRight(0, 12)[0 .. $]
14339 .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0, 0])
14343 // drop & dropBack test opslice ranges when available, popFront/popBack otherwise
14345 foreach (i
; 1 .. 13) assert(r5
.padRight(0, 12).drop(i
).walkLength
== 12 - i
);
14349 // Test nogc inference
14350 @safe @nogc pure unittest
14352 import std
.algorithm
.comparison
: equal
;
14354 static immutable r1
= [1, 2, 3, 4];
14355 static immutable r2
= [1, 2, 3, 4, 0, 0];
14356 assert(r1
.padRight(0, 6).equal(r2
));
14359 // Test back, popBack, and save
14360 @safe pure unittest
14362 import std
.algorithm
.comparison
: equal
;
14364 auto r1
= [1, 2, 3, 4].padRight(0, 6);
14365 assert(r1
.back
== 0);
14369 assert(r1
.equal([1, 2, 3, 4, 0]));
14370 assert(r2
.equal([1, 2, 3, 4, 0]));
14373 assert(r1
.back
== 3);
14374 assert(r1
.length
== 3);
14375 assert(r2
.length
== 5);
14376 assert(r2
.equal([1, 2, 3, 4, 0]));
14379 assert(r2
.length
== 4);
14380 assert(r2
[0] == 2);
14381 assert(r2
[1] == 3);
14382 assert(r2
[2] == 4);
14383 assert(r2
[3] == 0);
14384 assert(r2
.equal([2, 3, 4, 0]));
14387 assert(r2
.equal([2, 3, 4]));
14389 auto r3
= [1, 2, 3, 4].padRight(0, 6);
14399 // https://issues.dlang.org/show_bug.cgi?id=19042
14400 @safe pure unittest
14402 import std
.algorithm
.comparison
: equal
;
14404 assert([2, 5, 13].padRight(42, 10).chunks(5)
14405 .equal
!equal([[2, 5, 13, 42, 42], [42, 42, 42, 42, 42]]));
14407 assert([1, 2, 3, 4].padRight(0, 10)[7 .. 9].equal([0, 0]));
14411 This simplifies a commonly used idiom in phobos for accepting any kind of string
14412 parameter. The type `R` can for example be a simple string, chained string using
14413 $(REF chain, std,range), $(REF chainPath, std,path) or any other input range of
14416 Only finite length character ranges are allowed with this constraint.
14418 This template is equivalent to:
14420 isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R)
14424 $(REF isInputRange, std,range,primitives),
14425 $(REF isInfinite, std,range,primitives),
14426 $(LREF isSomeChar),
14427 $(REF ElementEncodingType, std,range,primitives)
14429 template isSomeFiniteCharInputRange(R
)
14431 import std
.traits
: isSomeChar
;
14433 enum isSomeFiniteCharInputRange
= isInputRange
!R
&& !isInfinite
!R
14434 && isSomeChar
!(ElementEncodingType
!R
);
14440 import std
.path
: chainPath
;
14441 import std
.range
: chain
;
14443 void someLibraryMethod(R
)(R argument
)
14444 if (isSomeFiniteCharInputRange
!R
)
14446 // implementation detail, would iterate over each character of argument
14449 someLibraryMethod("simple strings work");
14450 someLibraryMethod(chain("chained", " ", "strings", " ", "work"));
14451 someLibraryMethod(chainPath("chained", "paths", "work"));
14452 // you can also use custom structs implementing a char range