|
| 1 | +// Written in the D programming language. |
| 2 | +/** |
| 3 | +This is a submodule of $(MREF std, algorithm). |
| 4 | +It contains generic _iteration algorithms. |
| 5 | +$(SCRIPT inhibitQuickIndex = 1;) |
| 6 | +$(BOOKTABLE Cheat Sheet, |
| 7 | +$(TR $(TH Function Name) $(TH Description)) |
| 8 | +$(T2 uniq, |
| 9 | + Iterates over the unique elements in a range, which is assumed sorted.) |
| 10 | +) |
| 11 | +Copyright: Andrei Alexandrescu 2008-. |
| 12 | +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). |
| 13 | +Authors: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilya Yaroshenko (Mir & BetterC rework). |
| 14 | +Source: $(PHOBOSSRC std/algorithm/_iteration.d) |
| 15 | +Macros: |
| 16 | +T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) |
| 17 | + */ |
| 18 | +module mir.algorithm.iteration; |
| 19 | + |
| 20 | +import std.range.primitives: isInputRange, isBidirectionalRange, isInfinite, isForwardRange, ElementType; |
| 21 | +import mir.functional: naryFun; |
| 22 | +import mir.array.primitives; |
| 23 | + |
| 24 | +// uniq |
| 25 | +/** |
| 26 | +Lazily iterates unique consecutive elements of the given range (functionality |
| 27 | +akin to the $(HTTP wikipedia.org/wiki/_Uniq, _uniq) system |
| 28 | +utility). Equivalence of elements is assessed by using the predicate |
| 29 | +$(D pred), by default $(D "a == b"). The predicate is passed to |
| 30 | +$(REF nary, mir,functional), and can either accept a string, or any callable |
| 31 | +that can be executed via $(D pred(element, element)). If the given range is |
| 32 | +bidirectional, $(D uniq) also yields a |
| 33 | +`std,range,primitives`. |
| 34 | +Params: |
| 35 | + pred = Predicate for determining equivalence between range elements. |
| 36 | + r = An input range of elements to filter. |
| 37 | +Returns: |
| 38 | + An input range of |
| 39 | + consecutively unique elements in the original range. If `r` is also a |
| 40 | + forward range or bidirectional range, the returned range will be likewise. |
| 41 | +*/ |
| 42 | +Uniq!(naryFun!pred, Range) uniq(alias pred = "a == b", Range)(auto ref Range r) |
| 43 | +if (isInputRange!Range && is(typeof(naryFun!pred(r.front, r.front)) == bool)) |
| 44 | +{ |
| 45 | + return typeof(return)(r); |
| 46 | +} |
| 47 | + |
| 48 | +/// |
| 49 | +@safe unittest |
| 50 | +{ |
| 51 | + import std.algorithm.comparison : equal; |
| 52 | + import std.algorithm.mutation : copy; |
| 53 | + |
| 54 | + int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; |
| 55 | + assert(equal(uniq(arr), [ 1, 2, 3, 4, 5 ][])); |
| 56 | + |
| 57 | + // Filter duplicates in-place using copy |
| 58 | + arr.length -= arr.uniq().copy(arr).length; |
| 59 | + assert(arr == [ 1, 2, 3, 4, 5 ]); |
| 60 | + |
| 61 | + // Note that uniqueness is only determined consecutively; duplicated |
| 62 | + // elements separated by an intervening different element will not be |
| 63 | + // eliminated: |
| 64 | + assert(equal(uniq([ 1, 1, 2, 1, 1, 3, 1]), [1, 2, 1, 3, 1])); |
| 65 | +} |
| 66 | + |
| 67 | +/// |
| 68 | +struct Uniq(alias pred, Range) |
| 69 | +{ |
| 70 | + Range _input; |
| 71 | + |
| 72 | + // this()(auto ref Range input) |
| 73 | + // { |
| 74 | + // import std.meta: AliasSeq; |
| 75 | + // import mir.functional: forward; |
| 76 | + // AliasSeq!_input = forward!input; |
| 77 | + // } |
| 78 | + |
| 79 | + auto opSlice()() |
| 80 | + { |
| 81 | + return this; |
| 82 | + } |
| 83 | + |
| 84 | + void popFront()() |
| 85 | + { |
| 86 | + assert(!empty, "Attempting to popFront an empty uniq."); |
| 87 | + auto last = _input.front; |
| 88 | + do |
| 89 | + { |
| 90 | + _input.popFront(); |
| 91 | + } |
| 92 | + while (!_input.empty && pred(last, _input.front)); |
| 93 | + } |
| 94 | + |
| 95 | + @property ElementType!Range front()() |
| 96 | + { |
| 97 | + assert(!empty, "Attempting to fetch the front of an empty uniq."); |
| 98 | + return _input.front; |
| 99 | + } |
| 100 | + |
| 101 | + static if (isBidirectionalRange!Range) |
| 102 | + { |
| 103 | + void popBack()() |
| 104 | + { |
| 105 | + assert(!empty, "Attempting to popBack an empty uniq."); |
| 106 | + auto last = _input.back; |
| 107 | + do |
| 108 | + { |
| 109 | + _input.popBack(); |
| 110 | + } |
| 111 | + while (!_input.empty && pred(last, _input.back)); |
| 112 | + } |
| 113 | + |
| 114 | + @property ElementType!Range back()() |
| 115 | + { |
| 116 | + assert(!empty, "Attempting to fetch the back of an empty uniq."); |
| 117 | + return _input.back; |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + static if (isInfinite!Range) |
| 122 | + { |
| 123 | + enum bool empty = false; // Propagate infiniteness. |
| 124 | + } |
| 125 | + else |
| 126 | + { |
| 127 | + @property bool empty()() { return _input.empty; } |
| 128 | + } |
| 129 | + |
| 130 | + static if (isForwardRange!Range) |
| 131 | + { |
| 132 | + @property typeof(this) save()() { |
| 133 | + return typeof(this)(_input.save); |
| 134 | + } |
| 135 | + } |
| 136 | +} |
| 137 | + |
| 138 | +version(none) |
| 139 | +@safe unittest |
| 140 | +{ |
| 141 | + import std.algorithm.comparison : equal; |
| 142 | + import std.internal.test.dummyrange; |
| 143 | + import std.range; |
| 144 | + |
| 145 | + int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; |
| 146 | + auto r = uniq(arr); |
| 147 | + static assert(isForwardRange!(typeof(r))); |
| 148 | + |
| 149 | + assert(equal(r, [ 1, 2, 3, 4, 5 ][])); |
| 150 | + assert(equal(retro(r), retro([ 1, 2, 3, 4, 5 ][]))); |
| 151 | + |
| 152 | + foreach (DummyType; AllDummyRanges) |
| 153 | + { |
| 154 | + DummyType d; |
| 155 | + auto u = uniq(d); |
| 156 | + assert(equal(u, [1,2,3,4,5,6,7,8,9,10])); |
| 157 | + |
| 158 | + static assert(d.rt == RangeType.Input || isForwardRange!(typeof(u))); |
| 159 | + |
| 160 | + static if (d.rt >= RangeType.Bidirectional) |
| 161 | + { |
| 162 | + assert(equal(retro(u), [10,9,8,7,6,5,4,3,2,1])); |
| 163 | + } |
| 164 | + } |
| 165 | +} |
| 166 | + |
| 167 | +@safe unittest // https://issues.dlang.org/show_bug.cgi?id=17264 |
| 168 | +{ |
| 169 | + import std.algorithm.comparison : equal; |
| 170 | + |
| 171 | + const(int)[] var = [0, 1, 1, 2]; |
| 172 | + assert(var.uniq.equal([0, 1, 2])); |
| 173 | +} |
0 commit comments