www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Array index slicing

reply "bearophile" <bearophileHUGS lycos.com> writes:
In my D code I have inserted few times the same bug, that I show 
here with a simplified example.

I have to create a matrix like this:

0 10 20 30
0 11 21 31
0 12 22 32


There are many ways to inizialize a matrix like that, this is one 
way, but it's not complete:


import std.stdio;
void main() {
     auto M = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]];
     foreach (r, row; M)
         foreach (c, ref item; row)
             item = c * 10 + r;
     writefln("%(%s\n%)", M);
}


It outputs:

[0, 10, 20, 30]
[1, 11, 21, 31]
[2, 12, 22, 32]


To complete the algorithm, that is to not touch the first column, 
I can use just a slice in the second foreach, to not touch the 
first column:


import std.stdio;
void main() {
     auto M = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]];
     foreach (r, row; M)
         foreach (c, ref item; row[1 .. $])
             item = c * 10 + r;
     writefln("%(%s\n%)", M);
}


But this introduces the bug:

[0, 0, 10, 20]
[0, 1, 11, 21]
[0, 2, 12, 22]


Slicing 'row' from the second item I avoid to write on the first 
cells of each row, but the 'c' index doesn't get sliced, it 
starts from zero still. One correct version needs to increment c 
by one in the formula:

import std.stdio;
void main() {
     auto M = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]];
     foreach (r, row; M)
         foreach (c, ref item; row[1 .. $])
             item = (c + 1) * 10 + r;
     writefln("%(%s\n%)", M);
}


Another solution is to ignore the c==0:

     foreach (r, row; M)
         foreach (c, ref item; row)
             if (c != 0)
                 item = c * 10 + r;


I don't consider this a D/Phobos bug, but maybe there are library 
ways to help me (and others) avoid it.

In this enhancement request I have asked for an enumerate() in 
Phobos:
http://d.puremagic.com/issues/show_bug.cgi?id=5550

enumerate() is similar to the Python built-in iterable, it just 
yields pairs of (count,item), counting the items of a given 
iterable (range).

If enumerate() is well implemented it's one way to avoid that 
problem (other solutions are possible), now 'c' gets sliced, so 
it doesn't start from zero:


import std.stdio;
void main() {
     auto M = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]];
     foreach (r, row; M)
         foreach (c, ref item; enumerate(row)[1 .. $])
             item = c * 10 + r;
     writefln("%(%s\n%)", M);
}


Bye,
bearophile
Jul 13 2012
parent travert phare.normalesup.org (Christophe Travert) writes:
"bearophile" , dans le message (digitalmars.D:172300), a écrit :
 If enumerate() is well implemented it's one way to avoid that 
 problem (other solutions are possible), now 'c' gets sliced, so 
 it doesn't start from zero:
 
 import std.stdio;
 void main() {
      auto M = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]];
      foreach (r, row; M)
          foreach (c, ref item; enumerate(row)[1 .. $])
              item = c * 10 + r;
      writefln("%(%s\n%)", M);
 }

enumerate could be useful with retro too. You may want to change the order of the enumeration, but not the order of the indices.
Jul 16 2012