Thread overview
foreach automoatic counter?
Sep 21, 2015
French Football
Sep 21, 2015
Justin Whear
Sep 21, 2015
French Football
Sep 21, 2015
cym13
Sep 21, 2015
French Football
Sep 21, 2015
cym13
Sep 21, 2015
cym13
Sep 21, 2015
Jonathan M Davis
September 21, 2015
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...
September 21, 2015
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)
September 21, 2015
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.

September 21, 2015
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 [].
September 21, 2015
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.

September 21, 2015
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...
September 21, 2015
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...
September 21, 2015
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:

http://dlang.org/phobos/std_range.html#.lockstep

- Jonathan M Davis

September 22, 2015
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:
>
> http://dlang.org/phobos/std_range.html#.lockstep

As Justin Whear pointed out, enumerate is better when you have one range.

-Steve