On 1/18/22 14:08, forkit wrote:
> never use number ranges.. not ever! ;-)
>
> (except in combination with iota)
Indeed, the following is an elegant but slow (tested with dmd)
implementation with Phobos:
auto range(T)(T a, T b)
in (a <= b) {
import std.range : chain, iota, only;
return chain(iota(a, b), only(b));
}
But I like the following one better because it is fast and I think it
works correctly. However, I am reminded of one of the reasons why
exclusive ranges are better: It is not possible to represent an empty
range with the same syntax. For example, range(42, 42) includes 42.
Hmmm. Should range(42, 41) mean empty? Looks weird.
struct InclusiveRange(T) {
T front;
T last;
bool empty;
this(U)(in U front, in U last)
in (front <= last) {
this.front = front;
this.last = last;
this.empty = false;
}
void popFront() {
if (front == last) {
empty = true;
} else {
++front;
}
}
}
auto inclusiveRange(T)(T first, T last) {
return InclusiveRange!T(first, last);
}
unittest {
// Impossible to be empty
import std.algorithm : equal;
auto r = inclusiveRange(42, 42);
assert(!r.empty);
assert(r.equal([42]));
}
unittest {
// Can represent all values of a type
import std.range : ElementType;
import std.algorithm : sum;
auto r = inclusiveRange(ubyte.min, ubyte.max);
static assert(is(ElementType!(typeof(r)) == ubyte));
assert(r.sum == (ubyte.max * (ubyte.max + 1)) / 2);
}
unittest {
// Really inclusive
import std.algorithm : sum;
assert(inclusiveRange(1, 10).sum == 55);
}
unittest {
// Works with negative values
import std.algorithm : equal;
assert(inclusiveRange(-3, 3).equal([-3, -2, -1, 0, 1, 2, 3]));
assert(inclusiveRange(-30, -27).equal([-30, -29, -28, -27]));
}
import std.stdio;
void main() {
}
Ali