www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - foreach automoatic counter?

reply French Football <ff gmrle.com> writes:
Going through a book on coding in D, 
http://ddili.org/ders/d.en/foreach.html , I find the following 
very useful feature:

When two names are specified in the names section [with a plain 
array], they represent an automatic counter and the value of the 
element, respectively:
     foreach (i, element; array) {
         writeln(i, ": ", element);
     }

I understand that foreach is built on top of a for loop... I'm 
just wondering why I can't access the automatic counter from a 
doubly linked list, or an associative array, or some range? It's 
pretty common for me to have to rewrite foreach loops to be for 
loops when I get to the bottom and realize I need to know where 
in the sequence I am...
Sep 21 2015
next sibling parent reply Justin Whear <justin economicmodeling.com> writes:
On Mon, 21 Sep 2015 15:38:38 +0000, French Football wrote:

 Going through a book on coding in D,
 http://ddili.org/ders/d.en/foreach.html , I find the following very
 useful feature:
 
 When two names are specified in the names section [with a plain array],
 they represent an automatic counter and the value of the element,
 respectively:
      foreach (i, element; array) {
          writeln(i, ": ", element);
      }
 
 I understand that foreach is built on top of a for loop... I'm just
 wondering why I can't access the automatic counter from a doubly linked
 list, or an associative array, or some range? It's pretty common for me
 to have to rewrite foreach loops to be for loops when I get to the
 bottom and realize I need to know where in the sequence I am...
With an associative array the foreach becomes: foreach (key, value; aa) For arbitrary ranges you can use std.range.enumerate like this: foreach (i, element; someRange.enumerate)
Sep 21 2015
parent reply French Football <ff gmrle.com> writes:
On Monday, 21 September 2015 at 15:54:06 UTC, Justin Whear wrote:

On Monday, 21 September 2015 at 15:58:12 UTC, cym13 wrote:
 
Thankyou! .enumerate lets me iterate over a container with a counter. --Related tangential question... If I have a DList, how do I insert into the middle of it? I'm trying .insertAfter but it wants a range and apparently I can only slice an entire Dlist with [].
Sep 21 2015
parent reply cym13 <cpicard openmailbox.org> writes:
On Monday, 21 September 2015 at 16:32:25 UTC, French Football 
wrote:
 On Monday, 21 September 2015 at 15:54:06 UTC, Justin Whear 
 wrote:

 On Monday, 21 September 2015 at 15:58:12 UTC, cym13 wrote:
 
Thankyou! .enumerate lets me iterate over a container with a counter. --Related tangential question... If I have a DList, how do I insert into the middle of it? I'm trying .insertAfter but it wants a range and apparently I can only slice an entire Dlist with [].
I had to look into phobos sources (/usr/include/dlang/dmd/std/containers/dlist.d) to find a unittest, and judging from it it seems inserting in the middle of a DList just wasn't taken seriously. safe unittest { import std.algorithm : equal; auto dl = DList!string(["a", "b", "d"]); dl.insertAfter(dl[], "e"); // insert at the end assert(equal(dl[], ["a", "b", "d", "e"])); auto dlr = dl[]; dlr.popBack(); dlr.popBack(); dl.insertAfter(dlr, "c"); // insert after "b" assert(equal(dl[], ["a", "b", "c", "d", "e"])); } There is however a nicer method using std.ranges: void main(string[] args) { import std.stdio; import std.range; import std.container: DList; auto list = DList!int([1, 2, 3, 5]); list.insertBefore(list[].drop(3), 4); foreach(n ; list) writeln(n); } I hope this is helpful.
Sep 21 2015
parent reply French Football <ff gmrle.com> writes:
On Monday, 21 September 2015 at 19:23:38 UTC, cym13 wrote:
 On Monday, 21 September 2015 at 16:32:25 UTC, French Football 
 wrote:
 [...]
I had to look into phobos sources (/usr/include/dlang/dmd/std/containers/dlist.d) to find a unittest, and judging from it it seems inserting in the middle of a DList just wasn't taken seriously. [...]
Thankyou! But wow... talk about pulling teeth...
Sep 21 2015
parent cym13 <cpicard openmailbox.org> writes:
On Monday, 21 September 2015 at 22:24:22 UTC, French Football 
wrote:
 On Monday, 21 September 2015 at 19:23:38 UTC, cym13 wrote:
 On Monday, 21 September 2015 at 16:32:25 UTC, French Football 
 wrote:
 [...]
I had to look into phobos sources (/usr/include/dlang/dmd/std/containers/dlist.d) to find a unittest, and judging from it it seems inserting in the middle of a DList just wasn't taken seriously. [...]
Thankyou! But wow... talk about pulling teeth...
Such a unittest should normally be automatically included in online documentation when generating it, I don't know they were an exception...
Sep 21 2015
prev sibling next sibling parent cym13 <cpicard openmailbox.org> writes:
On Monday, 21 September 2015 at 15:38:40 UTC, French Football 
wrote:
 Going through a book on coding in D, 
 http://ddili.org/ders/d.en/foreach.html , I find the following 
 very useful feature:

 When two names are specified in the names section [with a plain 
 array], they represent an automatic counter and the value of 
 the element, respectively:
     foreach (i, element; array) {
         writeln(i, ": ", element);
     }

 I understand that foreach is built on top of a for loop... I'm 
 just wondering why I can't access the automatic counter from a 
 doubly linked list, or an associative array, or some range? 
 It's pretty common for me to have to rewrite foreach loops to 
 be for loops when I get to the bottom and realize I need to 
 know where in the sequence I am...
That's because this isn't really a counter. It makes more sens if you think of it as: foreach (key, value ; array) { ... } In the case of an array, the key to access directly a value is its index, and the array is read in its natural order so the 'key' part acts as a counter. If array is an associative array instead of an array, then you get the key and value as well. If you want a counter, you want to look at std.range.enumerate() which takes a range and returns a tuple (counter, element). The following example demonstrates the two usages with associative arrays: void main(string[] args) { import std.stdio; import std.range; bool[int] aa = [0:true, 1:true, 2:false]; writeln("Get keys and values from an AA"); foreach (key, value ; aa) { writeln(key, ": ", value); } writeln; writeln("Get a counter and the key of an AA"); foreach (count, key ; aa.byKey.enumerate) { writeln("count: ", count, " key: ", key, " value: ", aa[key]); } } Note that contrary to languages such as PHP, D's associative arrays are unordered so you can't use this counter as an index.
Sep 21 2015
prev sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Monday, September 21, 2015 15:38:38 French Football via Digitalmars-d-learn
wrote:
 Going through a book on coding in D,
 http://ddili.org/ders/d.en/foreach.html , I find the following
 very useful feature:

 When two names are specified in the names section [with a plain
 array], they represent an automatic counter and the value of the
 element, respectively:
      foreach (i, element; array) {
          writeln(i, ": ", element);
      }

 I understand that foreach is built on top of a for loop... I'm
 just wondering why I can't access the automatic counter from a
 doubly linked list, or an associative array, or some range? It's
 pretty common for me to have to rewrite foreach loops to be for
 loops when I get to the bottom and realize I need to know where
 in the sequence I am...
It's an index, not a counter. In the case of an array, it's what's used to access each element. In the case of an AA, it has no index. It's a set of unordered key-value pairs. So, if you did foreach(i, element; aa) {} what you're really getting is the key and value, not an index and its corresponding element. foreach(key, value; aa) {} would make more sense in that case, but it's merely a change in name. The types and what they represent are the same regardless. In the case of an input range, it doesn't have any kind of index. It's just a list of elements that keep getting popped off in order to iterate through them. And whether any kind of index would even make sense would depend on what the range represents. In the cases where it would make sense, the range is probably a random-access range, in which case, you can use indices explicitly if you want. In general though, if you want a counter for the range that you're indexing, then you can use lockstep to wrap the range, and then when you use it in foreach, you get the count and the element: - Jonathan M Davis
Sep 21 2015
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 9/21/15 6:49 PM, Jonathan M Davis via Digitalmars-d-learn wrote:
 In general though, if you want a counter for the
 range that you're indexing, then you can use lockstep to wrap the range, and
 then when you use it in foreach, you get the count and the element:


As Justin Whear pointed out, enumerate is better when you have one range. -Steve
Sep 22 2015