Re: number ranges

2022-01-21 Thread H. S. Teoh via Digitalmars-d-learn
> On Friday, 21 January 2022 at 17:25:20 UTC, Ali Çehreli wrote:
[...]
> > Additionally, just because we *provide* a step, now we *require*
> > division from all types (making it very cumbersome for user-defined
> > types).
[...]

It doesn't have to be this way. We could just use DbI to inspect whether
the incoming type supports division; if it does, we provide stepping,
otherwise, just plain ole iteration.

DbI rocks.


T

-- 
Doubt is a self-fulfilling prophecy.


Re: number ranges

2022-01-21 Thread Salih Dincer via Digitalmars-d-learn

On Friday, 21 January 2022 at 17:25:20 UTC, Ali Çehreli wrote:


Ouch! I tried the following code, my laptop got very hot, it's 
been centuries, and it's still running! :p


:)


```d
   size_t length() inout {
 auto len = 1 + (last - first) / step;
 return cast(size_t)len;
   }
```
Does that not return 1 for an empty range?


Yes, but it will never return an empty range:

```d
  enum e = 1;
  auto o = inclusiveRange(e, e); // only one element
  assert(!o.empty);
  assert(o.length == e);
  assert(o.equal([e]));
```

Additionally, just because we *provide* a step, now we 
*require* division from all types (making it very cumbersome 
for user-defined types).


I don't quite understand what you mean?

Salih



Re: number ranges

2022-01-21 Thread Ali Çehreli via Digitalmars-d-learn

On 1/21/22 08:58, Salih Dincer wrote:

> ```d
> auto inclusiveRange(T)(T f, T l, T s = cast(T)0)
> in(!isBoolean!T) {

'in' contracts are checked at runtime. The one above does not make sense 
because you already disallow compilation for 'bool' below.


You could add a template constraint there:

auto inclusiveRange(T)(T f, T l, T s = cast(T)0)
if(!isBoolean!T) {

(Note 'if' vs. 'in'.)

However, people who instantiate the struct template directly would 
bypass that check anyway.


>static assert(!isBoolean!T, "\n
>Cannot be used with bool type\n");
>if(!s) s++;
>return InclusiveRange!T(f, l, s);
> }

>bool opBinaryRight(string op:"in")(T rhs) {
>  foreach(r; this) {
>if(r == rhs) return true;
>  }
>  return false;
>}

Ouch! I tried the following code, my laptop got very hot, it's been 
centuries, and it's still running! :p


  auto looong = inclusiveRange(ulong.min, ulong.max);
  foreach (l; looong) {
assert(l in looong);
  }

>size_t length() inout {
>  auto len = 1 + (last - first) / step;
>  return cast(size_t)len;
>}

Does that not return 1 for an empty range?

Additionally, just because we *provide* a step, now we *require* 
division from all types (making it very cumbersome for user-defined types).


>// Pi Number Test
>auto GregorySeries = inclusiveRange!double(1, 0x1.0p+27, 2);

Very smart! ;) So, this type can support floating point values if we use 
that syntax.


Ali



Re: number ranges

2022-01-21 Thread Salih Dincer via Digitalmars-d-learn

On Thursday, 20 January 2022 at 16:33:20 UTC, Ali Çehreli wrote:


So if we add the 1.0 value after 0.900357627869 to be 
*inclusive*, then that last step would not be 0.3 anymore. 
(Thinking about it, step would mess up things for integral 
types as well; so, it must be checked during construction.)


The other obvious issue in the output is that a floating point 
iota cannot be bidirectional because the element values would 
be different.


The test that did not pass now passes. There is the issue of 
T.min being excluded from the property list for the double type. 
I tried to solve it, how is it?


Salih

```d
auto inclusiveRange(T)(T f, T l, T s = cast(T)0)
in(!isBoolean!T) {
  static assert(!isBoolean!T, "\n
  Cannot be used with bool type\n");
  if(!s) s++;
  return InclusiveRange!T(f, l, s);
}

struct InclusiveRange(T) {
  private:
T first, last;
bool empty_;

  public:
T step;

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

  bool opBinaryRight(string op:"in")(T rhs) {
foreach(r; this) {
  if(r == rhs) return true;
}
return false;
  }

  auto save() inout { return this; }
  bool empty() inout { return empty_; }
  T front() inout { return first; }
  T back() inout { return last; }

  void popFront() {
if(!empty) {
  if(last >= first + step) {
first += step;
  } else {
empty_ = true;
if(T.max <= first + step) {
  first += step;
}
  }
}
  }

  void popBack() {
if(!empty) {
  if(first <= last-step) {
last -= step;
  } else {
empty_ = true;
if(!T.max >= last - step) {
  last -= step;
}
  }
}
  }

  size_t length() inout {
auto len = 1 + (last - first) / step;
return cast(size_t)len;
  }
}

import std.algorithm, std.math;
import std.range, std.traits;
import std.stdio, std.format, std.conv;

void main() {
  // Pi Number Test
  auto GregorySeries = inclusiveRange!double(1, 0x1.0p+27, 2);
  double piNumber = 0;
  foreach(e, n; GregorySeries.enumerate) {
if(e & 1) piNumber -= 1/n;
else piNumber += 1/n;
  }
  writefln!"%.21f (constant)"(PI);
  writefln!"%.21f (calculated)"(piNumber * 4);

} unittest {
  // Should not be possible to have an empty range

  auto r = inclusiveRange(ubyte.min, ubyte.max);
  static assert(is(ElementType!(typeof(r)) == ubyte));

  assert(r.sum == (ubyte.max * (ubyte.max + 1)) / 2);
}
```




Re: number ranges

2022-01-20 Thread Ali Çehreli via Digitalmars-d-learn

On 1/19/22 21:24, Salih Dincer wrote:

> ```d
>size_t length() inout {
>  //return last_ - first_ + 1 - empty_;/*
>  auto len = 1 + last_ - first_;
>  return cast(size_t)len;//*/
>}
> ```

Good catch but we can't ignore '- empty_'. Otherwise an empty range will 
return 1.


> But it only works on integers.

After fixing the size_t issue, it should work on user-defined types as 
well. In fact, it is better to leave the return type as auto so that it 
works with user-defined types that support the length expression but is 
a different type like e.g. MyDiffType.


Having said that, floating point types don't make sense with the 
semantics of a *bidirectional and inclusive* range. :) Let's see how it 
looks for ranges where the step size is 0.3:


import std.stdio;

void main() {
  float beg = 0.0;
  float end = 1.0;
  float step = 0.3;

  writeln("\nIncrementing:");
  for (float f = beg; f <= end; f += step) {
report(f);
  }

  writeln("\nDecrementing:");
  for (float f = end; f >= beg; f -= step) {
report(f);
  }
}

void report(float f) {
  writefln!"%.16f"(f);
}

Here is the output:

Incrementing:
0.
0.300119209290
0.600238418579
0.900357627869
<-- Where is 1.0?
Decrementing:
1.
0.699880790710
0.399761581421
0.099642372131
<-- Where is 0.0?

So if we add the 1.0 value after 0.900357627869 to be *inclusive*, 
then that last step would not be 0.3 anymore. (Thinking about it, step 
would mess up things for integral types as well; so, it must be checked 
during construction.)


The other obvious issue in the output is that a floating point iota 
cannot be bidirectional because the element values would be different.


Ali



Re: number ranges

2022-01-19 Thread Salih Dincer via Digitalmars-d-learn

Hi,

It looks so delicious.  Thank you.

On Wednesday, 19 January 2022 at 18:59:10 UTC, Ali Çehreli wrote:


And adding length() was easy as well.

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


It doesn't matter as we can't use a 3rd parameter.  But it 
doesn't work for any of these types: real, float, double.


My solution:
```d
  size_t length() inout {
//return last_ - first_ + 1 - empty_;/*
auto len = 1 + last_ - first_;
return cast(size_t)len;//*/
  }
```
But it only works on integers. In this case, we have two options! 
The first is to require the use of integers, other 3 parameter 
usage:

```d
// ...
  size_t length() inout {
auto len = 1 + (last - front) / step;
return cast(size_t)len;
  }
} unittest {
  enum { ira = 0.1,
 irb = 2.09,
 irc = 0.11
   }
  auto test = inclusiveRange(ira, irb, irc);
  assert(test.count == 19);

  auto arr = iota(ira, irb, irc).array;
  assert(test.length == arr.length);
}
```

Salih


Re: number ranges

2022-01-19 Thread Ali Çehreli via Digitalmars-d-learn

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



Re: number ranges

2022-01-19 Thread Salih Dincer via Digitalmars-d-learn

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

```d
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
}
```


Re: number ranges

2022-01-18 Thread forkit via Digitalmars-d-learn

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..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...




Re: number ranges

2022-01-18 Thread Tejas via Digitalmars-d-learn

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..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...


Re: number ranges

2022-01-18 Thread Tejas via Digitalmars-d-learn

On Tuesday, 18 January 2022 at 17:58:54 UTC, H. S. Teoh wrote:
On Tue, Jan 18, 2022 at 04:02:42PM +, Tejas via 
Digitalmars-d-learn wrote: [...]
Newer languages nowadays use `start..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)
}
```


Re: number ranges

2022-01-18 Thread Era Scarecrow via Digitalmars-d-learn

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.


Re: number ranges

2022-01-18 Thread Ali Çehreli via Digitalmars-d-learn

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



Re: number ranges

2022-01-18 Thread forkit via Digitalmars-d-learn

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)



Re: number ranges

2022-01-18 Thread Ali Çehreli via Digitalmars-d-learn

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



Re: number ranges

2022-01-18 Thread forkit via Digitalmars-d-learn

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


Newer languages nowadays use `start..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));
}




Re: number ranges

2022-01-18 Thread H. S. Teoh via Digitalmars-d-learn
On Tue, Jan 18, 2022 at 04:02:42PM +, Tejas via Digitalmars-d-learn wrote:
[...]
> Newer languages nowadays use `start.. 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


Re: number ranges

2022-01-18 Thread Tejas via Digitalmars-d-learn

On Monday, 17 January 2022 at 22:48:17 UTC, H. S. Teoh wrote:
On Mon, Jan 17, 2022 at 10:35:30PM +, forkit via 
Digitalmars-d-learn wrote:

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

[...]

If I were able to write a compiler, my compiler would warn you:

"This is ill-advised and you should know better! Please 
rewrite this."


:-D  If *I* were to write a compiler, it'd come with a GC 
built-in. It'd throw up 90% of programs you feed it with the 
error "this program is garbage, please throw it away and write 
something better". :-D



T


Newer languages nowadays use `start..think it's something we should follow?


Re: number ranges

2022-01-17 Thread H. S. Teoh via Digitalmars-d-learn
On Mon, Jan 17, 2022 at 10:35:30PM +, forkit via Digitalmars-d-learn wrote:
> On Monday, 17 January 2022 at 22:28:10 UTC, H. S. Teoh wrote:
> > 
> > If I ever needed to foreach over 1-based indices, I'd write it this
> > way in order to avoid all confusion:
> > 
> > foreach (i; 1 .. 5 + 1)
> > {
> > }
> > 
> > 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. ;-)
[...]
> If I were able to write a compiler, my compiler would warn you:
> 
> "This is ill-advised and you should know better! Please rewrite this."

:-D  If *I* were to write a compiler, it'd come with a GC built-in. It'd
throw up 90% of programs you feed it with the error "this program is
garbage, please throw it away and write something better". :-D


T

-- 
If blunt statements had a point, they wouldn't be blunt...


Re: number ranges

2022-01-17 Thread forkit via Digitalmars-d-learn

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


If I ever needed to foreach over 1-based indices, I'd write it 
this way in order to avoid all confusion:


foreach (i; 1 .. 5 + 1)
{
}

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. ;-)



T


If I were able to write a compiler, my compiler would warn you:

"This is ill-advised and you should know better! Please rewrite 
this."




Re: number ranges

2022-01-17 Thread H. S. Teoh via Digitalmars-d-learn
On Mon, Jan 17, 2022 at 10:22:19PM +, forkit via Digitalmars-d-learn wrote:
[...]
> I think it's fair to say, that I'm familiar with 0-based indexing ;-)
> 
> my concern was with the 1..5 itself.
> 
> In terms of what makes sense, it actually makes more sense not to use
> it, at all ;-)

If I ever needed to foreach over 1-based indices, I'd write it this way
in order to avoid all confusion:

foreach (i; 1 .. 5 + 1)
{
}

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. ;-)


T

-- 
Change is inevitable, except from a vending machine.


Re: number ranges

2022-01-17 Thread forkit via Digitalmars-d-learn

On Monday, 17 January 2022 at 22:06:47 UTC, H. S. Teoh wrote:


Basically,

foreach (i; a .. b)

is equivalent to:

for (auto i = a; i < b; i++)

Just think of that way and it will make sense.



I think it's fair to say, that I'm familiar with 0-based indexing 
;-)


my concern was with the 1..5 itself.

In terms of what makes sense, it actually makes more sense not to 
use it, at all ;-)




Re: number ranges

2022-01-17 Thread H. S. Teoh via Digitalmars-d-learn
On Mon, Jan 17, 2022 at 09:37:31PM +, forkit via Digitalmars-d-learn wrote:
> On Monday, 17 January 2022 at 11:58:18 UTC, Paul Backus wrote:
> > 
> > This kind of half-open interval, which includes the lower bound but
> > excludes the upper bound, is used in programming because it lets you
> > write
> > 
> > foreach (i; 0 .. array.length) writef("%s ", array[i]);
> > 
> > ...without going past the end of the array.
> 
> Yes. But the intent here is clearly stated and cannot be misunderstood
> -> array.length
> 
> Whereas 1..5 is just an opportunity to shoot yourself in the foot.

The compiler cannot discern intent. Both `5` and `array.length` are
expressions, as far as the compiler is concerned. So is `5 +
(array.length - sin(x))/2*exp(array2.length)`, for that matter.  The
compiler does not understand what the programmer may have intended; it
simply follows what the spec says.

Of course, to a *human* the semantics of `1..5` can be totally confusing
if you're not used to it.  The bottom-line is, in D (and in other C-like
languages) you just have to get used to 0-based indexing, because
ultimately it actually makes more sense than the 1-based counting scheme
we were taught in school. 1-based counting schemes are full of
exceptions and off-by-1 errors; it's needlessly complex and hard for the
mortal brain to keep track of all the places where you have to add or
subtract 1.  Whereas in 0-based index schemes, you *always* count from
0, and you always use `<` to check your bounds, and you can do
arithmetic with indices just by adding and subtracting as usual, without
off-by-1 errors.

Basically,

foreach (i; a .. b)

is equivalent to:

for (auto i = a; i < b; i++)

Just think of that way and it will make sense.

And never ever write 1..n unless you actually intend to skip the first
element. Remember: 0-based counting, not 1-based counting. You always
start from 0, and count up to (but not including) n.  Which also means
you should always write `<`, never write `<=`. So your upper bound is
always the element *past* the last one. I.e., it's the index at which a
new element would be added if you were appending to your list. I.e., the
index at which a new element should be added is simply array.length (not
array.length+1 or array.length-1 or any of that error-prone crap that
nobody can remember).

If you adhere to these simple rules, you'll never need to add or
subtract 1 to your counters, loop indices, and lengths (because nobody
can remember when to do that, so not having to do it significantly
reduces the chances of bugs).


T

-- 
People say I'm arrogant, and I'm proud of it.


Re: number ranges

2022-01-17 Thread forkit via Digitalmars-d-learn

On Monday, 17 January 2022 at 11:58:18 UTC, Paul Backus wrote:


This kind of half-open interval, which includes the lower bound 
but excludes the upper bound, is used in programming because it 
lets you write


foreach (i; 0 .. array.length) writef("%s ", array[i]);

...without going past the end of the array.


Yes. But the intent here is clearly stated and cannot be 
misunderstood -> array.length


Whereas 1..5 is just an opportunity to shoot yourself in the foot.

"the heretic must be cast out not because of the probability that 
he is wrong but because of the possibility that he is right." - 
Edsger W. Dijkstra


Re: number ranges

2022-01-17 Thread Salih Dincer via Digitalmars-d-learn

On Monday, 17 January 2022 at 11:58:18 UTC, Paul Backus wrote:

On Monday, 17 January 2022 at 10:24:06 UTC, forkit wrote:
Edsger W. Dijkstra, a well-known academic computer scientist, 
has written in more detail about the advantages of this kind of 
interval: 
https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html


Thank you for this valuable information you have given. There is 
a nice feature used in uniform():

```d
import std;

enum a = 9;
enum b = 10;

void main() {
  auto sonYok = generate!(() => uniform!"[)"(a, b)).take(10);
   sonYok.writeln; // only 9 (default)

  auto ilk_son = generate!(() => uniform!"[]"(a, b)).take(10);
   ilk_son.writeln; // may contain 9 & 10

  auto orta = generate!(() => uniform!"()"(a, b + 1)).take(10);
   orta.writeln; // only 10

  auto ilkYok = generate!(() => uniform!"(]"(a, b + 1)).take(10);
   ilkYok.writeln; // Does not contain 9
}
```
It would be nice if this feature, which we set up with templates, 
could be applied everywhere in D. Because sometimes it is needed.


As for other, I never used this feature until I got used to it. 
Of course, it's practical like this, it will do 10 reps:


```foreach(_;0..11)```

Salih


Re: number ranges

2022-01-17 Thread Paul Backus via Digitalmars-d-learn

On Monday, 17 January 2022 at 10:24:06 UTC, forkit wrote:

so I'm wondering why the code below prints:

1 2 3 4

and not

1 2 3 4 5

as I would expect.

foreach (value; 1..5) writef("%s ", value);


This kind of half-open interval, which includes the lower bound 
but excludes the upper bound, is used in programming because it 
lets you write


foreach (i; 0 .. array.length) writef("%s ", array[i]);

...without going past the end of the array.

Edsger W. Dijkstra, a well-known academic computer scientist, has 
written in more detail about the advantages of this kind of 
interval: 
https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html



also, why is this not possible:

int[] arr = 1..5.array;


The `lower .. upper` syntax only works in foreach loops. If you 
want to create a range of numbers like this in another context, 
you must use the library function std.range.iota:


import std.range: iota;
int[] arr = iota(1, 5).array;

(Why "iota"? Because in APL, the Greek letter iota (ι) is used to 
create a range of numbers like this.)


number ranges

2022-01-17 Thread forkit via Digitalmars-d-learn

so I'm wondering why the code below prints:

1 2 3 4

and not

1 2 3 4 5

as I would expect.

foreach (value; 1..5) writef("%s ", value);

also, why is this not possible:

int[] arr = 1..5.array;