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...
- Kirk McDonald (2/8) Jun 08 2006 Sweet!
- Oskar Linde (24/81) Jun 08 2006
- 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
- Andrei Khropov (4/15) Jun 18 2006 Absolutely agree. My vote for it!
Instead of: for(int i = 1_000; i < 1_000_000; i++) {} How about: 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
Dave wrote:Instead of: for(int i = 1_000; i < 1_000_000; i++) {} How about: 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. - DaveThis 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 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: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");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
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
Derek Parnell wrote:On Wed, 07 Jun 2006 22:56:50 -0700, Kirk McDonald wrote:Sweet!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...
Jun 08 2006
Derek Parnell wrote:On Wed, 07 Jun 2006 22:56:50 -0700, Kirk McDonald wrote:<replace>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;</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:Thanks for the info. on Python. Since there's precedent with Python, I guess '0 based' ranges would settle that.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 iThough 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
Dave skrev:Kirk McDonald wrote:<snip>Dave 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. 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 OlssonThough 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.
Jun 13 2006
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