number ranges

Ali Çehreli acehreli at yahoo.com
Wed Jan 19 18:59:10 UTC 2022


On 1/19/22 04:51, Salih Dincer wrote:

 > Is it okay to swap places instead of throwing an error?

I would be happier if my potential mistake is caught instead of the 
library doing something on its own.

 > Let's also
 > implement BidirectionalRange, if okay.

I had started experimenting with that as well. The implementation below 
does not care if popFront() or popBack() are called on empty ranges. The 
programmer must be careful. :)

 > "Does it reverse the result
 > in case ```a > b``` like we
 > did with foreach_reverse()"

No, it should not reverse the direction that way because we already have 
retro. :)

   inclusiveRange(1, 10).retro;

Improving the range as a BidirectionalRange requires three more 
functions: save(), back(), and popBack().

I am also removing the silly 'const' qualifiers from member functions 
because a range object is supposed to be mutated. I came to that 
conclusion after realizing that save() cannot be 'const' because it 
break isForwardRange (which is required by isBidirectionalRange). Ok, I 
will change all 'const's to 'inout's in case someone passes an object to 
a function that takes by 'const' and just applies e.g. empty() on it.

And adding length() was easy as well.

Finally, I have provided property functions instead of allowing direct 
access to members.

struct InclusiveRange(T) {
   import std.format : format;

   T first_;
   T last_;
   bool empty_;

   this(U)(in U first, in U last)
   in (first <= last, format!"Invalid range: [%s,%s]."(first, last)) {
     this.first_ = first;
     this.last_ = last;
     this.empty_ = false;
   }

   T front() inout {
     return first_;
   }

   bool empty() inout {
     return empty_;
   }

   void popFront() {
     if (first_ == last_) {
       empty_ = true;

     } else {
       ++first_;
     }
   }

   auto save() inout {
     return this;
   }

   T back() inout {
     return last_;
   }

   void popBack() {
     if (first_ == last_) {
       empty_ = true;

     } else {
       --last_;
     }
   }

   size_t length() inout {
     return last_ - first_ + 1 - empty_;
   }
}

auto inclusiveRange(T)(in T first, in T last) {
   return InclusiveRange!T(first, last);
}

unittest {
   // Invalid range should throw
   import std.exception : assertThrown;

   assertThrown!Error(inclusiveRange(2, 1));
}

unittest {
   // Should not be possible to have an empty range
   import std.algorithm : equal;

   auto r = inclusiveRange(42, 42);
   assert(!r.empty);
   assert(r.equal([42]));
}

unittest {
   // Should be able to 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 {
   // Should produce the last value
   import std.algorithm : sum;

   assert(inclusiveRange(1, 10).sum == 55);
}

unittest {
   // Should work 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]));
}

unittest {
   // length should be correct
   import std.range : enumerate, iota;

   enum first = 5;
   enum last = 42;
   auto r = inclusiveRange(first, last);

   // Trusting iota's implementation
   size_t expectedLength = iota(first, last).length + 1;
   size_t i = 0;
   do {
     assert(r.length == expectedLength);
     r.popFront();
     --expectedLength;
   } while (!r.empty);
}

unittest {
   // Should provide the BidirectionalRange interface
   import std.range : retro;
   import std.algorithm : equal;

   auto r = inclusiveRange(1, 10);
   assert(!r.save.retro.equal(r.save));
   assert(r.save.retro.retro.equal(r.save));
}

void main() {
   import std.stdio;
   import std.range;

   writeln(inclusiveRange(1, 10));
   writeln(inclusiveRange(1, 10).retro);

   auto r = inclusiveRange(1, 11);
   while (true) {
     writefln!"%s .. %s  length: %s"(r.front, r.back, r.length);
     r.popFront();
     if (r.empty) {
       break;
     }
     r.popBack();
     if (r.empty) {
       break;
     }
   }
}

Ali



More information about the Digitalmars-d-learn mailing list