January 18, 2022
On Tue, Jan 18, 2022 at 04:02:42PM +0000, Tejas via Digitalmars-d-learn wrote: [...]
> Newer languages nowadays use `start..<end` to denote the intent, think it's something we should follow?

I've never seen that before.  Which languages use that?


T

-- 
"If you're arguing, you're losing." -- Mike Thomas
January 18, 2022
On Tuesday, 18 January 2022 at 16:02:42 UTC, Tejas wrote:
>
> Newer languages nowadays use `start..<end` to denote the intent, think it's something we should follow?

I've decided to avoid using number ranges 'directly', and instead use a wrapper function...


auto range(T:T)(T a, T b)
{
    import std.range : iota;
    return iota(a, (b+1));
}


January 18, 2022
On 1/18/22 12:43, forkit wrote:

> wrapper function...
>
>
> auto range(T:T)(T a, T b)
> {
>      import std.range : iota;
>      return iota(a, (b+1));
> }

Needs a little more work to be correct. The following produces and empty range. ;)

  range(uint.min, uint.max)

Also, is it important for the result to be the same as T? For example, even if T is ubyte, because b+1 is 'int', the range will produce ints.

Ali

January 18, 2022
On Tuesday, 18 January 2022 at 20:50:06 UTC, Ali Çehreli wrote:
>
> Needs a little more work to be correct. The following produces and empty range. ;)
>
>   range(uint.min, uint.max)
>
> Also, is it important for the result to be the same as T? For example, even if T is ubyte, because b+1 is 'int', the range will produce ints.
>
> Ali

a change of mind...

never use number ranges.. not ever!  ;-)

(except in combination with iota)

January 18, 2022
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

January 19, 2022

On Monday, 17 January 2022 at 22:28:10 UTC, H. S. Teoh wrote:

>

This will immediately make whoever reads the code (i.e., myself after 2 months :D) wonder, "why +1?" And the answer will become clear and enlightenment ensues. ;-)

In those cases i find myself rewriting said code. Generally to say for(int i=1; i<=5; i++) or something, where it includes the last one but doesn't add oddities that doesn't explain the magic numbers or odd +1.

Then again the big issue probably comes from people coming from BASIC of some description where the FOR A=1 TO 5, where index starts at 1 and includes the number listed; And you aren't given other conditions to test against. It really does take a little getting used to.

Maybe we don't use Qbasic or 8bit MSBASIC much anymore, but Visual Basic and legacy code grandfathers those in, and maybe a few other interpreted languages too.

January 19, 2022

On Tuesday, 18 January 2022 at 17:58:54 UTC, H. S. Teoh wrote:

>

On Tue, Jan 18, 2022 at 04:02:42PM +0000, Tejas via Digitalmars-d-learn wrote: [...]

>

Newer languages nowadays use start..<end to denote the intent, think it's something we should follow?

I've never seen that before. Which languages use that?

T

In Nim for example:

for n in 5 .. 9:  #Both 5 and 9 are included
  echo n

echo ""

for n in 5 ..< 9: #5 is included but 9 is excluded
  echo n

In Odin also:

for i in 0..<10 {
	fmt.println(i)
}
// or
for i in 0..9 {
	fmt.println(i)
}
January 19, 2022

On Tuesday, 18 January 2022 at 20:43:08 UTC, forkit wrote:

>

On Tuesday, 18 January 2022 at 16:02:42 UTC, Tejas wrote:

>

Newer languages nowadays use start..<end to denote the intent, think it's something we should follow?

I've decided to avoid using number ranges 'directly', and instead use a wrapper function...

auto range(T:T)(T a, T b)
{
import std.range : iota;
return iota(a, (b+1));
}

Aww, come on; it ain't that bad...

January 19, 2022
On Wednesday, 19 January 2022 at 03:00:49 UTC, Tejas wrote:
> On Tuesday, 18 January 2022 at 20:43:08 UTC, forkit wrote:
>> On Tuesday, 18 January 2022 at 16:02:42 UTC, Tejas wrote:
>>>
>>> Newer languages nowadays use `start..<end` to denote the intent, think it's something we should follow?
>>
>> I've decided to avoid using number ranges 'directly', and instead use a wrapper function...
>>
>>
>> auto range(T:T)(T a, T b)
>> {
>>     import std.range : iota;
>>     return iota(a, (b+1));
>> }
>
> Aww, come on; it ain't that bad...

Well that depends on entirely on what the code is doing ;-)

The key is to *always* *remember* the stop index is not included.

I sure hope they 'remembered' this in the code running on that telescope floating out into open space...

January 19, 2022

On Tuesday, 18 January 2022 at 23:13:14 UTC, Ali Çehreli wrote:

>

But I like the following one better because
it is fast and I think it works correctly.

Is it okay to swap places instead of throwing an error? Let's also implement BidirectionalRange, if okay. This great struct will now run 4x4 like a Jeep. 😀

Moreover, the iota doesn't even care if define char type. And no throwing an error.

The real question to ask is:

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

Salih

import std;

struct InclusiveRange(T) {
  T front, last;

  this(U)(in U front, in U last) {
    this.front = front;
    this.last = last;

    if(empty) toogleFrontLast();
  }

  bool empty() {
    return front > last;
  }

  void popFront() {
    if(!empty) ++front;
  }

  T back() {
    return last;
  }

  void popBack() {
    if(!empty) --last;
  }

  void toogleFrontLast() {
    auto temp = this.last;
    this.last = this.front;
    this.front = temp;
  }
}

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

    enum a = 8; // ASCII 80: P
    enum b = 7; // ASCII 70: F

    alias type = char;
    alias test = inclusiveRange;

void main() {
  string str; // for tests...
  auto io = iota!type(a * 10, b * 10);
       io.writeln("\n", typeof(io).stringof, "\n");

  str = to!string(io);
  assert(str == "[]"); // OMG, why?

  auto ir = test!type(a * 10, b * 10);
       ir.writeln("\n", typeof(ir).stringof, "\n");

  str = to!string(ir);
  assert(str == "FGHIJKLMNOP"); // Ok

  foreach_reverse(c; ir) str ~= c;
  assert(str == "FGHIJKLMNOPPONMLKJIHGF"); // Ok
}