www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Enhancing foreach

reply "ixid" <nuaccount gmail.com> writes:
A very minor change that would be elegant and easy for beginners:

foreach(i;5)
     //stuff

Allowing just a single number to mean the end point and a default 
starting point of zero is assumed, just as with iota it's 
possible to write it iota(5) or even 5.iota, it assumes unless 
otherwise specified that you mean 0 to be the starting point. 
Would this be a reasonable enhancement request for me to make or 
does it collide with something?
Jan 08 2013
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-01-09 05:38, ixid wrote:
 A very minor change that would be elegant and easy for beginners:

 foreach(i;5)
      //stuff

 Allowing just a single number to mean the end point and a default
 starting point of zero is assumed, just as with iota it's possible to
 write it iota(5) or even 5.iota, it assumes unless otherwise specified
 that you mean 0 to be the starting point. Would this be a reasonable
 enhancement request for me to make or does it collide with something?
I don't see what this would gain over: foreach (i ; 0 .. 5) -- /Jacob Carlborg
Jan 09 2013
parent reply "ixid" <nuaccount gmail.com> writes:
On Wednesday, 9 January 2013 at 08:01:05 UTC, Jacob Carlborg 
wrote:
 On 2013-01-09 05:38, ixid wrote:
 A very minor change that would be elegant and easy for 
 beginners:

 foreach(i;5)
     //stuff

 Allowing just a single number to mean the end point and a 
 default
 starting point of zero is assumed, just as with iota it's 
 possible to
 write it iota(5) or even 5.iota, it assumes unless otherwise 
 specified
 that you mean 0 to be the starting point. Would this be a 
 reasonable
 enhancement request for me to make or does it collide with 
 something?
I don't see what this would gain over: foreach (i ; 0 .. 5)
Just like the terser iota, it's more elegant and easier for newer users. The fewer symbols there are in code the easier it is to parse for the user (up to a point). It's also more consistent with iota. It also means that a foreach that iterates a function return value can be written in the same manner whether it returns an array or an integer, again more consistency.
Jan 09 2013
parent reply "ixid" <nuaccount gmail.com> writes:
 Just like the terser iota, it's more elegant and easier for 
 newer users. The fewer symbols there are in code the easier it 
 is to parse for the user (up to a point). It's also more 
 consistent with iota. It also means that a foreach that 
 iterates a function return value can be written in the same 
 manner whether it returns an array or an integer, again more 
 consistency.
It would be even nicer to be able to write for(i;5) and have it behave as foreach.
Jan 09 2013
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 01/09/2013 09:07 AM, ixid wrote:
 Just like the terser iota,
iota got it wrong. I would expect new users to be confused by iota(5). Is that "ends at 5" or "begins with 5"? The latter is more consistent with how the function parameters at the and can have default values. So it is likely to be perceived as the following. Ignoring that iota is a template: /* ... */ iota(size_t begin, size_t end = size_t.max);
 it's more elegant and easier for newer
 users.
I think having multiple ways of doing the same thing is confusing.
 It would be even nicer to be able to write for(i;5) and have it behave
 as foreach.
Maybe nicer but not helpful. What is the meaning of the following? for (i; ++j) { /* ... */ } Did I forget something or is it "from zero to the incremented value of j"? Ali
Jan 09 2013
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Ali Çehreli:

 iota got it wrong. I would expect new users to be confused by 
 iota(5). Is that "ends at 5" or "begins with 5"? The latter is 
 more consistent with how the function parameters at the and can 
 have default values. So it is likely to be perceived as the 
 following. Ignoring that iota is a template:

   /* ... */ iota(size_t begin, size_t end = size_t.max);
I was the one that asked for the iota(5) syntax. The name iota comes from APL, where a single argument is supported, with similar meaning. But the idea of a single argument comes from Python:
 range(5)
[0, 1, 2, 3, 4] I have taught Python, and newbies understand/know that a range(5) means a range of five items. The only problems is knowing where it starts. But when you teach Python you say that indexes are 0-based, so they quickly remember that the range of five items starts from zero. In D iota does the same. And so far I have had no problems from using iota this way. So I think iota got it right. Instead, what I have had to tell friends reading my D code is what the heck "iota" means. The meaning of the word "range" in Python is simpler to guess. Bye, bearophile
Jan 09 2013
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 01/09/2013 10:06 AM, bearophile wrote:

 Instead, what I have had to tell friends reading my D code is what the
 heck "iota" means. The meaning of the word "range" in Python is simpler
 to guess.
Same here. :) Ali
Jan 09 2013
prev sibling parent reply "ixid" <nuaccount gmail.com> writes:
I think you're rather a long way away from beginners to see how 
beginners would think. iota is behaving like the Python range 
syntax which beginners seem to find quite easy to use. A beginner 
thinks 'it goes up to that number', the thought process you 
outlined is that of someone learning their second or third 
language.

   for (i; ++j) {
       /* ... */
   }

 Did I forget something or is it "from zero to the incremented 
 value of j"?
Is that any different to the current for loops not doing what you meant if you don't type things? It doesn't seem subtle nor an unexpected behaviour, there's no shortage of cases where if you omit bits of code it'll do something else. If this was supposed to be a normal for loop the user's forgotten to give i a type and a boundary condition and the second semi-colon, that's three errors, the space of different behaviours you can get to in any given piece of code by making three errors is probably quite high.
Jan 09 2013
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 01/09/2013 10:08 AM, ixid wrote:

 for (i; ++j) {
 /* ... */
 }

 Did I forget something or is it "from zero to the incremented value of
 j"?
 If this was supposed to be a normal for loop
 the user's forgotten to give i a type and a boundary condition and the
 second semi-colon, that's three errors, the space of different
 behaviours you can get to in any given piece of code by making three
 errors is probably quite high.
I think the name 'i' would make one think that there has been three mistakes. (I admit that I chose it that way. ;) ) In fact, there was a single mistake: a forgotten semicolon (and a space that comes with it for readability): for (; i; ++j) { /* ... */ } Ali
Jan 09 2013
prev sibling parent "Jesse Phillips" <Jessekphillips+D gmail.com> writes:
On Wednesday, 9 January 2013 at 17:07:50 UTC, ixid wrote:
 It would be even nicer to be able to write for(i;5) and have it 
 behave as foreach.
I hated java's choice to use for as foreach. It does not prepare me for the semantics of what I'm about to read. And when I go to write it and don't remember, is it for(Type thing; range) or for(Type thing, range)? no it is for(Type thing : range) I was already heavily using foreach in do before Java got one.
Jan 09 2013
prev sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Wednesday, January 09, 2013 05:38:16 ixid wrote:
 A very minor change that would be elegant and easy for beginners:
 
 foreach(i;5)
 //stuff
 
 Allowing just a single number to mean the end point and a default
 starting point of zero is assumed, just as with iota it's
 possible to write it iota(5) or even 5.iota, it assumes unless
 otherwise specified that you mean 0 to be the starting point.
 Would this be a reasonable enhancement request for me to make or
 does it collide with something?
I would argue that the mistake is that iota(5) works. That's not at all clear, and foreach(i; 5) //stuff is no better. Regardless, all such an enhancement would do is save you a little bit of typing. It adds no actual functionality to the language, so I _really_ don't think that it makes sense to implement anything like that. - Jonathan M Davis
Jan 09 2013
next sibling parent reply "ixid" <nuaccount gmail.com> writes:
On Wednesday, 9 January 2013 at 23:15:10 UTC, Jonathan M Davis 
wrote:
 On Wednesday, January 09, 2013 05:38:16 ixid wrote:
 A very minor change that would be elegant and easy for 
 beginners:
 
 foreach(i;5)
 //stuff
 
 Allowing just a single number to mean the end point and a 
 default
 starting point of zero is assumed, just as with iota it's
 possible to write it iota(5) or even 5.iota, it assumes unless
 otherwise specified that you mean 0 to be the starting point.
 Would this be a reasonable enhancement request for me to make 
 or
 does it collide with something?
I would argue that the mistake is that iota(5) works. That's not at all clear, and foreach(i; 5) //stuff is no better. Regardless, all such an enhancement would do is save you a little bit of typing. It adds no actual functionality to the language, so I _really_ don't think that it makes sense to implement anything like that. - Jonathan M Davis
Regardless of this particular suggestion's value, I think you're wrong to dismiss readable terseness and saving typing as mattering, it's one of D's advantages over C++ that it makes a lot of things far easier to do and understand because they're not a horrid mess.
Jan 09 2013
next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, January 10, 2013 03:04:56 ixid wrote:
 Regardless of this particular suggestion's value, I think you're
 wrong to dismiss readable terseness and saving typing as
 mattering, it's one of D's advantages over C++ that it makes a
 lot of things far easier to do and understand because they're not
 a horrid mess.
Terseness can be nice, but not at the cost of clarity (and there's a major loss of clarity with your suggestion). There's also a definite cost to providing multiple ways to do exactly the same thing. It makes the language more complicated and increases its cognitive load. It's that much worse if the construct in question is relatively rare. One case where tersness has been quite valuable is being able to omit parens for single-argument templates, but unlike your foreach suggestion, it's relatively obvious what's going on, and it's used quite frequently (sometimes multiple times in a single statement). Iterating over a list of numbers like with foreach(i; 0 .. 5) {} is much less frequent in comparison. There is a cost to any feature to the language, and so features must pull their own weight. _All_ that this suggestion does is save a few characters in a construct that in my experience is occasionally useful but not heavily used. And it comes with a cost to clarity. - Jonathan M Davis
Jan 09 2013
prev sibling next sibling parent reply "Peter Summerland" <p.summerland gmail.com> writes:
On Thursday, 10 January 2013 at 02:04:57 UTC, ixid wrote:
 On Wednesday, 9 January 2013 at 23:15:10 UTC, Jonathan M Davis 
 wrote:
 On Wednesday, January 09, 2013 05:38:16 ixid wrote:
 A very minor change that would be elegant and easy for 
 beginners:
 
 foreach(i;5)
 //stuff
 
 Allowing just a single number to mean the end point and a 
 default
 starting point of zero is assumed, just as with iota it's
 possible to write it iota(5) or even 5.iota, it assumes unless
 otherwise specified that you mean 0 to be the starting point.
 Would this be a reasonable enhancement request for me to make 
 or
 does it collide with something?
I would argue that the mistake is that iota(5) works. That's not at all clear, and foreach(i; 5) //stuff is no better. Regardless, all such an enhancement would do is save you a little bit of typing. It adds no actual functionality to the language, so I _really_ don't think that it makes sense to implement anything like that. - Jonathan M Davis
Regardless of this particular suggestion's value, I think you're wrong to dismiss readable terseness and saving typing as mattering, it's one of D's advantages over C++ that it makes a lot of things far easier to do and understand because they're not a horrid mess.
I don't think Jonathan was (merely) dismissing readable terseness and saving typing. IMO he had more pertinent reasons why foreach(i; 0 .. 5) {} is very nice, *as is*.
Jan 09 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Thursday, 10 January 2013 at 03:29:21 UTC, Peter Summerland 
wrote:
 I don't think Jonathan was (merely) dismissing readable 
 terseness and saving typing. IMO he had more pertinent reasons 
 why

 foreach(i; 0 .. 5)
 {}

 is very nice, *as is*.
The only thing I'd want to be able to do is: //---- foreach ( ; 0 .. 5) { writeln("hello"); } //---- If I don't need a named variable, why force me to define a symbol? http://d.puremagic.com/issues/show_bug.cgi?id=9009 I know I could just use "i" and move on, but when code starts getting complex, and you already have i, j, k, ii, dummy etc..., it can make a difference.
Jan 10 2013
parent reply =?UTF-8?B?UmFwaGHDq2wgSmFrc2U=?= <raphael.jakse gmail.com> writes:
Le 10/01/2013 10:23, monarch_dodra a écrit :
 On Thursday, 10 January 2013 at 03:29:21 UTC, Peter Summerland wrote:

 The only thing I'd want to be able to do is:
 //----
 foreach ( ; 0 .. 5)
 {
      writeln("hello");
 }
 //----

 If I don't need a named variable, why force me to define a symbol?
 http://d.puremagic.com/issues/show_bug.cgi?id=9009

 I know I could just use "i" and move on, but when code starts getting
 complex, and you already have i, j, k, ii, dummy etc..., it can make a
 difference.
What about : foreach (0 .. 5) { writeln("hello"); } ?
Jan 10 2013
parent reply "Ary Borenszweig" <ary esperanto.org.ar> writes:
On Thursday, 10 January 2013 at 17:36:15 UTC, Raphaël Jakse wrote:
 Le 10/01/2013 10:23, monarch_dodra a écrit :
 On Thursday, 10 January 2013 at 03:29:21 UTC, Peter Summerland 
 wrote:

 The only thing I'd want to be able to do is:
 //----
 foreach ( ; 0 .. 5)
 {
     writeln("hello");
 }
 //----

 If I don't need a named variable, why force me to define a 
 symbol?
 http://d.puremagic.com/issues/show_bug.cgi?id=9009

 I know I could just use "i" and move on, but when code starts 
 getting
 complex, and you already have i, j, k, ii, dummy etc..., it 
 can make a
 difference.
What about : foreach (0 .. 5) { writeln("hello"); } ?
What about: 5 { writeln("hello"); } It could even work with floats! 1.5 { writeln("nice"); } prints: nice ni
Jan 15 2013
parent =?UTF-8?B?UmFwaGHDq2wgSmFrc2U=?= <raphael.jakse gmail.com> writes:
Le 15/01/2013 19:41, Ary Borenszweig a écrit :
 On Thursday, 10 January 2013 at 17:36:15 UTC, Raphaël Jakse wrote:
 Le 10/01/2013 10:23, monarch_dodra a écrit :
 On Thursday, 10 January 2013 at 03:29:21 UTC, Peter Summerland wrote:

 The only thing I'd want to be able to do is:
 //----
 foreach ( ; 0 .. 5)
 {
     writeln("hello");
 }
 //----

 If I don't need a named variable, why force me to define a symbol?
 http://d.puremagic.com/issues/show_bug.cgi?id=9009

 I know I could just use "i" and move on, but when code starts getting
 complex, and you already have i, j, k, ii, dummy etc..., it can make a
 difference.
What about : foreach (0 .. 5) { writeln("hello"); } ?
What about: 5 { writeln("hello"); } It could even work with floats! 1.5 { writeln("nice"); } prints: nice ni
D should definitively implement the GWPM (Gess What the Programmer Meant) compile-time feature. That would be awesome.
Jan 15 2013
prev sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Thursday, 10 January 2013 at 02:04:57 UTC, ixid wrote:
 Regardless of this particular suggestion's value, I think 
 you're wrong to dismiss readable terseness and saving typing as 
 mattering, it's one of D's advantages over C++ that it makes a 
 lot of things far easier to do and understand because they're 
 not a horrid mess.
The difference is that D allows doing in a single instruction what C++ would require several. It is actually less *code*, less duplication, less potential for bugs: New FUNCTIONALITY. What you are proposing isn't much of that, it's just making you type less...
Jan 10 2013
prev sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 I would argue that the mistake is that iota(5) works. That's 
 not at all clear,
Clarity is not an absolute property, it's also correlated to the frequency of usage and commonality. A symbol like a cross "+" doesn't have much of intrinsic meaning, but most people know what it means from their study of mathematics. Unlike the "+" symbol the word "range" has some meaning, suggests an interval or range. range() is used all the time in Python, almost in every loop, it's extremely common, so all Python programmers learn the usage of range() (newbies need a bit of time to learn the meaning of the optional third argument, but as far as I know they don't stumble on range(5) once they know all things in Python start from zero unless told differently). In my code iota() is common, even if it's surely not as common as range() in Python. I think it's common enough that you remember that iota(5) is a range of five items. And D too is 0-based. (and iota in APL accepted one argument). Bye, bearophile
Jan 09 2013
parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, January 10, 2013 03:19:51 bearophile wrote:
 In my code iota() is common, even if it's surely not as common as
 range() in Python. I think it's common enough that you remember
 that iota(5) is a range of five items. And D too is 0-based. (and
 iota in APL accepted one argument).
I almost never use iota. It's useful upon occasion, but in my experience, it's _far_ more common to iterate over ranges of items rather than just over a list of numbers. Clearly, you're doing things very differently than I am if you use iota that much. Regardless, I don't think that loss to clarity is worth the characters saved when providing only one argument to iota. - Jonathan M Davis
Jan 09 2013
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 I almost never use iota.
Then a problem is in the difference in usage frequency. My first answer is that you probably should use iota more, because it's handy in many situations :-) If I grep my code I count almost 120 usages of iota. I think D programmers that have some Python experience learn to use iota quickly, also because the semantics was designed to be the same as python3 range().
 It's useful upon occasion, but in my experience, it's
 _far_ more common to iterate over ranges of items rather than 
 just over a list of numbers.
Iterating on ranges is common, but numbers are items :-) There are lot of legitimate uses of iota in D code. One of the tens of legitimate usages of iota() is to replace the missing enumerate of Phobos, see Issue 5550.
 Clearly, you're doing things very differently than I am if you 
 use iota that much.
I am using ranges (including iota ranges) a lot in D code.
 Regardless, I don't think that loss to clarity is worth the 
 characters saved when providing only one argument to iota.
As I have said, clarity can't be considered ignoring the usage frequency. Why don't you use "add(,)" instead of "+"? Add or addition is more clear, but the usage of a symbol is acceptable here because it's both a very common operation and because because most people know the meaning of that symbol from math studies. Another fact to consider is that functional style D code doesn't ask for the same compactness as non-functional D code. In "regular" D code adding few more chars is not a problem. In functional-style code you usually want to write more compact code, to keep a logical chunk of code in a line (or in a group of small lines if you add newlines before the dots). Functional languages are terse not just for arbitrary reasons, but because terseness seems to go well with that kind of coding. When you use iota terseness is more important than "normal" D code.
Jan 09 2013
parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, January 10, 2013 03:51:34 bearophile wrote:
 My first
 answer is that you probably should use iota more, because it's
 handy in many situations :-)
I use it when I need it, but that's quite rare and almost always in unit tests. I might operate on ranges of numbers, but if so, they generally come from somewhere and represent real data of some kind. They're not generated. Other than needing to generate ranges for testing, I can't remember the last time that I needed to generate a range of numbers for anything. - Jonathan M Davis
Jan 09 2013
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 I use it when I need it,
I think there many usages of iota that you are missing. To spot those cases you probably need to train yourself a bit. A Python programmer doesn't need that training.
 I can't remember the last
 time that I needed to generate a range of numbers for anything.
There are hundreds of use cases. Some examples: To create a row of an identity matrix (j is the row number): iota(n).map!(i => cast(T)(i == j))() A short matrix transpose: auto transpose(T)(in T[][] m) { return iota(m[0].length).map!(i => transversal(m, i))(); } To write progressive numbers in a textual table: string result = format("N. flags: %d\n %(%d%)", n_flags, iota(1, n_columns + 1)); Or to generate the ticks on the table rows: auto line = iota(m).map!(_ => std.array.replicate(["+--"], n))() To generate a table of random numbers: auto randoms = iota(n).map!(_ => uniform(0.0, 1.0))().array(); More usages with random numbers: bool isIn(int) { return hypot(uniform(0.0, 1.0), uniform(0.0, 1.0)) <= 1; } double pi(in int n) { return 4.0 * count!isIn(iota(n)) / n; } Bye, bearophile
Jan 09 2013
parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, January 10, 2013 04:43:48 bearophile wrote:
 There are hundreds of use cases. Some examples:
[snip] And I don't normally need to do any of those things in my programs. I think that you must be doing very different things with your typical programs than I'm doing with my typical programs. - Jonathan M Davis
Jan 09 2013