## digitalmars.D - Another foreach idea.

• Dave (23/23) Jun 07 2006 Instead of:
• Kirk McDonald (74/107) Jun 07 2006 This is very much like Python's behavior. In that language, there isn't
• Kirk McDonald (45/48) Jun 07 2006 Oh! I did some thinking and improved it a little. It's a struct now, so
• zadok (9/66) Jun 07 2006 make your struct predefined in the language with the syntaxic sugar
• Derek Parnell (137/139) Jun 07 2006 Hope you don't mind but I turned your code into a templated version...
• Dave (8/26) Jun 08 2006 Thanks for the info. on Python. Since there's precedent with Python, I
• Fredrik Olsson (17/30) Jun 13 2006 I can take this as an opportunity to yet again voice my opinion for
```Instead of:

for(int i = 1_000; i < 1_000_000; i++) {}

foreach(idx; 1_000 .. 1_000_000)
{
// 1_000 to 999_999 or 1_000 to 1_000_000?
// type inferred 'idx' would be an int or long
//  depending on 32 bit or 64 bit hardware
}

or simply:

size_t x = 1_000_000;

foreach(idx; x)
{
// 0 to 999_999 or 1 to 1_000_000?
// type inferred 'idx' is size_t
}

The rational would be not only for brevity but also the reasons given
here: http://digitalmars.com/d/faq.html#foreach

I can see the problems already - 0 or 1 based? 0 based to be consistent
with how many for loops are written now and with array indexing (even
though presumably you'd then use foreach with the array itself)? Or 1
based because then it would be WYSIWYG.

- Dave
```
Jun 07 2006
Kirk McDonald <kirklin.mcdonald gmail.com> writes:
```Dave wrote:

for(int i = 1_000; i < 1_000_000; i++) {}

foreach(idx; 1_000 .. 1_000_000)
{
// 1_000 to 999_999 or 1_000 to 1_000_000?
// type inferred 'idx' would be an int or long
//  depending on 32 bit or 64 bit hardware
}

or simply:

size_t x = 1_000_000;

foreach(idx; x)
{
// 0 to 999_999 or 1 to 1_000_000?
// type inferred 'idx' is size_t
}

The rational would be not only for brevity but also the reasons given
here: http://digitalmars.com/d/faq.html#foreach

I can see the problems already - 0 or 1 based? 0 based to be consistent
with how many for loops are written now and with array indexing (even
though presumably you'd then use foreach with the array itself)? Or 1
based because then it would be WYSIWYG.

- Dave

This is very much like Python's behavior. In that language, there isn't
a "for" loop as such, only a "foreach" loop. To loop a counter from 0 to
9, you'd write:

for i in range(10):
print i

Where "range" is a function that returns a list containing a range of
integers, with a footprint like:

range(start=0, end, step=1)

(Python allows default values in places that D does not.) There is also
an xrange function, which returns a generator rather than a whole list,
which can be more useful for large ranges.

To loop from 10 to 99, you'd write:

for i in range(10, 100):
print i

In short, all you need is a "range" function that returns an array of
integers, or (alternately) a generator... something like this:

private import std.stdio;

class range {
protected:
int m_start, m_end;
int m_step = 1;
public:
// Can't just specify (end, step), but there's nothing
// to be done for that.
this(int end) {
m_end = end;
}
this(int start, int end) {
m_start = start;
this(end);
}
this(int start, int end, int step) {
m_step = step;
this(start, end);
}

// Convenience functions
static range opCall(int end) {
return new range(end);
}
static range opCall(int start, int end) {
return new range(start, end);
}
static range opCall(int start, int end, int step) {
return new range(start, end, step);
}

// Compares whether i has reached the end
private bool cmp(int i) {
if (m_step > 0 && i < m_end) return true;
else if (m_step < 0 && i > m_end) return true;
else return false;
}

int opApply(int delegate(inout int) dg) {
int result = 0;
for (int i=m_start; this.cmp(i); i+=m_step) {
result = dg(i);
if (result) break;
}
return result;
}
}

void main() {
foreach(i; range(10)) {
writefln("%s", i);
}
foreach(j; range(10, 20)) {
writefln("%s", j);
}
foreach(k; range(0, 10, 2)) {
writefln("%s", k);
}
}

Though honestly, a regular for loop isn't much of a burden.

-Kirk McDonald
```
Jun 07 2006
Kirk McDonald <kirklin.mcdonald gmail.com> writes:
```Kirk McDonald wrote:
In short, all you need is a "range" function that returns an array of
integers, or (alternately) a generator... something like this:

Oh! I did some thinking and improved it a little. It's a struct now, so
it doesn't need to touch the GC. And factory functions are maybe a
little more clear than static opCalls (also, there are two, now).

private import std.stdio;

struct range_struct {
public:
int m_start, m_end, m_step;

// Compares whether i has reached the end
private bool cmp(int i) {
if (m_step > 0 && i < m_end) return true;
else if (m_step < 0 && i > m_end) return true;
else return false;
}

int opApply(int delegate(inout int) dg) {
int result = 0;
for (int i=m_start; this.cmp(i); i+=m_step) {
result = dg(i);
if (result) break;
}
return result;
}
}

range_struct range(int start, int end, int step=1) {
range_struct r;
r.m_start = start;
r.m_end   = end;
r.m_step  = step;
return r;
}

range_struct range(int end) {
return range(0, end, 1);
}

void main() {
foreach(i; range(10)) {
writefln("%s", i);
}
foreach(j; range(10, 20)) {
writefln("%s", j);
}
foreach(k; range(0, 10, 2)) {
writefln("%s", k);
}
}

-Kirk McDonald
```
Jun 07 2006
```Kirk McDonald wrote:
Kirk McDonald wrote:
In short, all you need is a "range" function that returns an array of
integers, or (alternately) a generator... something like this:

Oh! I did some thinking and improved it a little. It's a struct now, so
it doesn't need to touch the GC. And factory functions are maybe a
little more clear than static opCalls (also, there are two, now).

private import std.stdio;

struct range_struct {
public:
int m_start, m_end, m_step;

// Compares whether i has reached the end
private bool cmp(int i) {
if (m_step > 0 && i < m_end) return true;
else if (m_step < 0 && i > m_end) return true;
else return false;
}

int opApply(int delegate(inout int) dg) {
int result = 0;
for (int i=m_start; this.cmp(i); i+=m_step) {
result = dg(i);
if (result) break;
}
return result;
}
}

range_struct range(int start, int end, int step=1) {
range_struct r;
r.m_start = start;
r.m_end   = end;
r.m_step  = step;
return r;
}

range_struct range(int end) {
return range(0, end, 1);
}

void main() {
foreach(i; range(10)) {
writefln("%s", i);
}
foreach(j; range(10, 20)) {
writefln("%s", j);
}
foreach(k; range(0, 10, 2)) {
writefln("%s", k);
}
}

-Kirk McDonald

make your struct predefined in the language with the syntaxic sugar
start..end
and this would be fine to compile:

range r = 0..9;
assert(r == range(0, 9));

r = range(0, 9, 2);
char[] str = "0123456789";
assert(str[r] == "02468");
```
Jun 07 2006
Derek Parnell <derek psych.ward> writes:
```On Wed, 07 Jun 2006 22:56:50 -0700, Kirk McDonald wrote:

Kirk McDonald wrote:

Oh! I did some thinking and improved it a little. It's a struct now ...

Hope you don't mind but I turned your code into a templated version...

//------- file: ranger.d ----------------
struct range_struct(T) {
public:
T m_start, m_end, m_step;

// Compares whether i has reached the end
private bool cmp(T i) {
if (m_step > 0 && i < m_end) return true;
else if (m_step < 0 && i > m_end) return true;
else return false;
}

int opApply(int delegate(inout T) dg) {
int result = 0;
for (T i=m_start; this.cmp(i); i+=m_step) {
result = dg(i);
if (result) break;
}
return result;
}
int opApply(int delegate(inout int, inout T) dg) {
int result = 0;
int cnt;
for (T i=m_start; this.cmp(i); i+=m_step) {
result = dg(cnt, i);
if (result) break;
cnt++;
}
return result;
}
}
template range_t(T)
{
range_struct!(T) range(T start, T end, T step=1) {
range_struct!(T) r;
r.m_start = start;
r.m_end   = end;
r.m_step  = step;
return r;
}

range_struct!(T) range(T end) {
return range(0, end, 1);
}
}

alias range_t!(char).range range;
alias range_t!(int).range range;
alias range_t!(real).range range;
//--------- FILE END -----------

// -------file: test.d ---------
import std.stdio;
import ranger;

void main() {
foreach(i; range(10)) {
writefln("i %s", i);
}
foreach(j; range(10, 20)) {
writefln("j %s", j);
}
foreach(q, k; range(10.0, 0.0, -0.9)) {
writefln("k %s %s", q, k);
}
foreach(q, l; range('A', cast(char)('Z'+1))) {
writefln("l %s %s", q, l);
}
}
//--------- FILE END -----------

When run with ...

build c:\temp\test.d  -exec

I get ...
Path and Version : y:\util\build.exe v3.00(1964)
built on Tue Jun  6 10:38:29 2006

i 0
i 1
i 2
i 3
i 4
i 5
i 6
i 7
i 8
i 9
j 10
j 11
j 12
j 13
j 14
j 15
j 16
j 17
j 18
j 19
k 0 10
k 1 9.1
k 2 8.2
k 3 7.3
k 4 6.4
k 5 5.5
k 6 4.6
k 7 3.7
k 8 2.8
k 9 1.9
k 10 1
k 11 0.1
l 0 A
l 1 B
l 2 C
l 3 D
l 4 E
l 5 F
l 6 G
l 7 H
l 8 I
l 9 J
l 10 K
l 11 L
l 12 M
l 13 N
l 14 O
l 15 P
l 16 Q
l 17 R
l 18 S
l 19 T
l 20 U
l 21 V
l 22 W
l 23 X
l 24 Y
l 25 Z

Neat eh?

--
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Down with mediocrity!"
8/06/2006 4:41:43 PM
```
Jun 07 2006
Kirk McDonald <kirklin.mcdonald gmail.com> writes:
```Derek Parnell wrote:
On Wed, 07 Jun 2006 22:56:50 -0700, Kirk McDonald wrote:
Oh! I did some thinking and improved it a little. It's a struct now ...

Hope you don't mind but I turned your code into a templated version...

Sweet!
```
Jun 08 2006
Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
```Derek Parnell wrote:
On Wed, 07 Jun 2006 22:56:50 -0700, Kirk McDonald wrote:

Kirk McDonald wrote:

Oh! I did some thinking and improved it a little. It's a struct now ...

Hope you don't mind but I turned your code into a templated version...

//------- file: ranger.d ----------------
struct range_struct(T) {
public:
T m_start, m_end, m_step;

// Compares whether i has reached the end
private bool cmp(T i) {
if (m_step > 0 && i < m_end) return true;
else if (m_step < 0 && i > m_end) return true;
else return false;
}

int opApply(int delegate(inout T) dg) {
int result = 0;
for (T i=m_start; this.cmp(i); i+=m_step) {
result = dg(i);
if (result) break;
}
return result;
}
int opApply(int delegate(inout int, inout T) dg) {
int result = 0;
int cnt;
for (T i=m_start; this.cmp(i); i+=m_step) {
result = dg(cnt, i);
if (result) break;
cnt++;
}
return result;
}
}

<replace>
template range_t(T)
{
range_struct!(T) range(T start, T end, T step=1) {
range_struct!(T) r;
r.m_start = start;
r.m_end   = end;
r.m_step  = step;
return r;
}

range_struct!(T) range(T end) {
return range(0, end, 1);
}
}

alias range_t!(char).range range;
alias range_t!(int).range range;
alias range_t!(real).range range;

</replace>

Replacing the above code with the following (kind of ugly) code makes it
work for any type that is comparable, implements += and (this is
limiting but possibly workaroundable with some assumptions) assignable
from 1 and 0.

template range(T,T2,T3=int) {
static assert(is(T2:T), "range arg 2 must be convertible to T");
static assert(is(T3:T), "range arg 3 must be convertible to T");
range_struct!(T) range(T start, T2 end, T3 step=1) {
range_struct!(T) r;
r.m_start = start;
r.m_end   = end;
r.m_step  = step;
return r;
}
}

template range(T) {
range_struct!(T) range(T end) {
return .range!(T,T,T)(0,end,1);
}
}

/Oskar
```
Jun 08 2006
```Kirk McDonald wrote:
Dave wrote:
I can see the problems already - 0 or 1 based? 0 based to be
consistent with how many for loops are written now and with array
indexing (even though presumably you'd then use foreach with the array
itself)? Or 1 based because then it would be WYSIWYG.

This is very much like Python's behavior. In that language, there isn't
a "for" loop as such, only a "foreach" loop. To loop a counter from 0 to
9, you'd write:

for i in range(10):
print i

Thanks for the info. on Python. Since there's precedent with Python, I
guess '0 based' ranges would settle that.

Though honestly, a regular for loop isn't much of a burden.

You're right, it isn't, until you run into some time critical loops that
you'd like to leave to the compiler to optimize. And a loop-invariant
condition and 'range' could allow foreach to better do things like loop
unrolling or use vector instructions, I think. It'd be one more thing
that sets D apart too.

-Kirk McDonald

```
Jun 08 2006
Fredrik Olsson <peylow gmail.com> writes:
```Dave skrev:
Kirk McDonald wrote:

Dave wrote:

<snip>
Though honestly, a regular for loop isn't much of a burden.

You're right, it isn't, until you run into some time critical loops that
you'd like to leave to the compiler to optimize. And a loop-invariant
condition and 'range' could allow foreach to better do things like loop
unrolling or use vector instructions, I think. It'd be one more thing
that sets D apart too.

I can take this as an opportunity to yet again voice my opinion for
implementing ranges and sets as basic types of D. Templates are good and
all, but as Walter writes himself on proper handling of floating point
and infinity:
1. Building it in to the core language means the core language knows
what a floating point infinity is. Being layered in templates, typedefs,
casts, const bit patterns, etc., it doesn't know what it is, and is
unlikely to give sensible error messages if misused.
2. A side effect of (1) is it is unlikely to be able to use it
effectively in constant folding and other optimizations.

I think the same thing goes for ranges, ranged integer types and sets.
The expressive power and the productivity boost they give is just too
huge to be ignored. More work done with less code is _always_ good, less
code == less bugs to avoid, find, and correct.

// Fredrik Olsson
```
Jun 13 2006
"Andrei Khropov" <andkhropov nospam_mtu-net.ru> writes:
```Fredrik Olsson wrote:

I can take this as an opportunity to yet again voice my opinion for
implementing ranges and sets as basic types of D. Templates are good and all,
but as Walter writes himself on proper handling of floating point and
infinity:  1. Building it in to the core language means the core language
knows what a floating point infinity is. Being layered in templates,
typedefs, casts, const bit patterns, etc., it doesn't know what it is, and is
unlikely to give sensible error messages if misused.  2. A side effect of (1)
is it is unlikely to be able to use it effectively in constant folding and
other optimizations.

I think the same thing goes for ranges, ranged integer types and sets.

Absolutely agree. My vote for it!

--
AKhropov
```
Jun 18 2006