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

Reply via email to