Jump to page: 1 2
Thread overview
Multi dimensional array question.
Jul 11, 2010
dcoder
Jul 11, 2010
Simen kjaeraas
Jul 11, 2010
BCS
Jul 12, 2010
Heywood Floyd
Jul 12, 2010
bearophile
Jul 12, 2010
Heywood Floyd
Jul 12, 2010
bearophile
Jul 14, 2010
Heywood Floyd
Jul 14, 2010
Jonathan M Davis
Jul 15, 2010
bearophile
Jul 16, 2010
bearophile
Jul 16, 2010
Jonathan M Davis
Jul 16, 2010
Heywood Floyd
Jul 16, 2010
Tim Verweij
Jul 16, 2010
Mafi
Jul 16, 2010
Tim Verweij
Jul 17, 2010
Heywood Floyd
Jul 17, 2010
bearophile
July 11, 2010
Hello.

I'm wondering why in D if you declare a fixed multi dimensional array, you have to reverse the index order to access an element.  I know it has something to do with how tightly [] bind, but the consequence is that it seems so different to other languages, it makes it error prone.  So here's some code that compiles and works:



import std.stdio;

int main() {
  int[3][5] marr;
  int i = 0;

  foreach( j, ref a; marr) {
    writefln( "%s: a.length = %s", j, a.length);
    foreach( ref v; a) {
      v = i++;
    }
  }


  /* This doesn't work.  marr[0][>=3] is out of bounds!!

     writefln( "marr[0][0] = %s", marr[0][0]);
     writefln( "marr[0][1] = %s", marr[0][1]);
     writefln( "marr[0][2] = %s", marr[0][2]);
     writefln( "marr[0][3] = %s", marr[0][3]);
     writefln( "marr[0][4] = %s", marr[0][4]);


     writefln( "marr[1][0] = %s", marr[1][0]);
     writefln( "marr[1][1] = %s", marr[1][1]);
     writefln( "marr[1][2] = %s", marr[1][2]);
     writefln( "marr[1][3] = %s", marr[1][3]);
     writefln( "marr[1][4] = %s", marr[1][4]);
  */


  writefln( "marr[0][0] = %s", marr[0][0]);
  writefln( "marr[1][0] = %s", marr[1][0]);
  writefln( "marr[2][0] = %s", marr[2][0]);
  writefln( "marr[3][0] = %s", marr[3][0]);
  writefln( "marr[4][0] = %s", marr[4][0]);
  writefln( "");

  writefln( "marr[0][1] = %s", marr[0][1]);
  writefln( "marr[1][1] = %s", marr[1][1]);
  writefln( "marr[2][1] = %s", marr[2][1]);
  writefln( "marr[3][1] = %s", marr[3][1]);
  writefln( "marr[4][1] = %s", marr[4][1]);
  writefln( "");

  writefln( "marr[0][2] = %s", marr[0][2]);
  writefln( "marr[1][2] = %s", marr[1][2]);
  writefln( "marr[2][2] = %s", marr[2][2]);
  writefln( "marr[3][2] = %s", marr[3][2]);
  writefln( "marr[4][2] = %s", marr[4][2]);
  writefln( "");

  return 0;
}


Anyways, perhaps I am doing something wrong, or I have been writing too much C code.  What are your thoughts?
July 11, 2010
dcoder <gtdegamo@yahoo.com> wrote:

> I'm wondering why in D if you declare a fixed multi dimensional array, you
> have to reverse the index order to access an element.  I know it has something
> to do with how tightly [] bind, but the consequence is that it seems so
> different to other languages, it makes it error prone.

The idea is that T[] is an array of T, for any T. if T == int[3], T[]
would be int[3][]. Also, for indexing, given T[] arr. arr[i] peels off
the first layer, giving you a T.

If arr[i] used the first set of brackets, generic functions would be
thoroughly borked, as a T passed to a template as int[3] would lead to
different indexing from a T of int.

-- 
Simen
July 11, 2010
Hello dcoder,

> Hello.
> 
> I'm wondering why in D if you declare a fixed multi dimensional array,
> you have to reverse the index order to access an element.  

When declaring an array, the base type is getting wrapped. When using an array, the base types get unwrapped.
Because both forms place the [] as a subfix and in both cases the sub-type/expression needs to be contiguous it end up the way it is.

alias int[5] T

T[7] aa;
T a = aa[6];
int v = a[4];

or

(int[5])[7] aa;
int v = (aa[6])[4];


-- 
... <IXOYE><



July 12, 2010
This had me crazy. I ended up putting the brackets on the variable, like

  int marr[3][5];

then it worked like

  marr[2][4] = 9;
July 12, 2010
Heywood Floyd:
> This had me crazy. I ended up putting the brackets on the variable, like
>   int marr[3][5];
> then it worked like
>   marr[2][4] = 9;

That's present only for compatibility with C syntax, this means that you can use it to perform a quicker port of C code to D, but you are supposed to later convert it to D-style array definitions.
See also:
http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=113185

Bye,
bearophile
July 12, 2010
bearophile Wrote:

> Heywood Floyd:
> > This had me crazy. I ended up putting the brackets on the variable, like
> >   int marr[3][5];
> > then it worked like
> >   marr[2][4] = 9;
> 
> That's present only for compatibility with C syntax, this means that you can use it to perform a quicker port of C code to D, but you are supposed to later convert it to D-style array definitions.
> See also:
> http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=113185
> 
> Bye,
> bearophile

Aha, good to know! Thanks!
(So this might go away in the future? Or require a -cstyle compile flag?)

***

I have a feeling this "backwards"-array stuff is gonna be one of the things my brain will repel for as long as it can.

To me, it seems equal to saying "you declare a function like:


  void foo(string name, int age){
     //... }


And then call it by doing

   foo(90,"Benny")

and this makes sense because the arguments are actually pushed on the stack in reverse at runtime, so they arrive in the correct order in the function. And this is especially important with tuples." or something.

Ok, I understand this has some deep purpose that I still don't understand, I'm just, yeah. *tear*
(Although this is way out of my league: For instance, if a T is an int[3][4], then why can't T[string] be a int[string][3][4], and be accessed with arr["a"][2][3]? Seems just like a matter of taste?)

/HF

PS. I was not the thread creator. I just used the creator's terminology to "stay on topic". Maybe too on topic : ) Not important really. Sorry for the confusion. DS.


July 12, 2010
Heywood Floyd:
> Aha, good to know! Thanks!
> (So this might go away in the future? Or require a -cstyle compile flag?)

It's just an idea of mine, my weight in D design is near zero, and Walter doesn't love warnings. I don't know if C-style array definitions will ever go away from D, don't hold your breath :-) But in D code I suggest to replace them with D-style array definitions when possible.


> PS. I was not the thread creator. I just used the creator's terminology to "stay on topic". Maybe too on topic : ) Not important really. Sorry for the confusion. DS.

I am sorry, my error :-(

Bye,
bearophile
July 13, 2010
On Mon, 12 Jul 2010 17:23:16 -0400, Heywood Floyd wrote:

> bearophile Wrote:
> 
>> Heywood Floyd:
>> > This had me crazy. I ended up putting the brackets on the variable,
>> > like
>> >   int marr[3][5];
>> > then it worked like
>> >   marr[2][4] = 9;
>> 
>> That's present only for compatibility with C syntax, this means that you can use it to perform a quicker port of C code to D, but you are supposed to later convert it to D-style array definitions. See also: http://www.digitalmars.com/webnews/newsgroups.php?
art_group=digitalmars.D&article_id=113185
>> 
>> Bye,
>> bearophile
> 
> Aha, good to know! Thanks!
> (So this might go away in the future? Or require a -cstyle compile
> flag?)
> 
> ***
> 
> I have a feeling this "backwards"-array stuff is gonna be one of the things my brain will repel for as long as it can.
> 
> To me, it seems equal to saying "you declare a function like:
> 
> 
>   void foo(string name, int age){
>      //... }
> 
> 
> And then call it by doing
> 
>    foo(90,"Benny")
> 
> and this makes sense because the arguments are actually pushed on the stack in reverse at runtime, so they arrive in the correct order in the function. And this is especially important with tuples." or something.
> 
> Ok, I understand this has some deep purpose that I still don't understand, I'm just, yeah. *tear* (Although this is way out of my league: For instance, if a T is an int[3][4], then why can't T[string] be a int[string][3][4], and be accessed with arr["a"][2][3]? Seems just like a matter of taste?)


But then arrays would be different from all other types!  If you have an array of 3 Ts, that is written T[3], regardless of what T is.  Now consider these two cases:

   A. T is an int.  Then T[3] becomes int[3].

   B. T is an int[string].  Then T[3] becomes int[string][3].

In case A, the first element of the array is accessed like this:

   int[3] a;
   int firstA = a[0];

Since a is an array of int, firstA is of course an int.

But then, since b is an array of int[string], we have

   int[string][3] b;
   int[string] firstB = b[0];

If we again want to access element "foo" of the associative array which is firstB, we write firstB["foo"].  And so we have the following three ways to get to that element, which *must* be equivalent because that's how the language is defined:

   // Using firstB as an intermediate step
   int[string] firstB = b[0];
   int x = firstB["foo"];

   // Drop the intermediate variable firstB
   int x = (b[0])["foo"];

   // Drop the redundant parentheses
   int x = b[0]["foo"];

So you see, it can't be any other way than the way it is. :)

-Lars
July 14, 2010
Lars T. Kyllingstad Wrote:

> 
> But then arrays would be different from all other types!  If you have an array of 3 Ts, that is written T[3], regardless of what T is.  Now consider these two cases:
> 
>    A. T is an int.  Then T[3] becomes int[3].
> 
>    B. T is an int[string].  Then T[3] becomes int[string][3].
> 
> In case A, the first element of the array is accessed like this:
> 
>    int[3] a;
>    int firstA = a[0];
> 
> Since a is an array of int, firstA is of course an int.
> 
> But then, since b is an array of int[string], we have
> 
>    int[string][3] b;
>    int[string] firstB = b[0];
> 
> If we again want to access element "foo" of the associative array which is firstB, we write firstB["foo"].  And so we have the following three ways to get to that element, which *must* be equivalent because that's how the language is defined:
> 
>    // Using firstB as an intermediate step
>    int[string] firstB = b[0];
>    int x = firstB["foo"];
> 
>    // Drop the intermediate variable firstB
>    int x = (b[0])["foo"];
> 
>    // Drop the redundant parentheses
>    int x = b[0]["foo"];
> 
> So you see, it can't be any other way than the way it is. :)
> 
> -Lars

Thank you for the elaborate answer!

When you put it like that, it does make sense. But I'm sorry. I refuse. The reason I refuse is those examples are void of any higher semantic meaning. Once we add a semantic meaning, it simply becomes backwards:

int[MAX_WIDTH][MAX_HEIGHT] map2d;
map2d[x][y] = 9; // Wrong!

At least in my head, this is cognitive dissonance. To me, the language acts as if it's low-level semantics outweighs my high-level semantics and I should correct my thinking for that. I refuse! Seems to me it could just as well work as:

int[string][3] b;
int[3] firstB = b["foo"];
int i = firstB[0];
int j = (b["foo"])[0];
int k = b["foo"][0];

But I feel like I'm the only one feeling this, so I'll just let it go and hope my dear C-style arrays stay in :)

BR
/HF

PS. Never thought I'd find a reason to love C.. DS.

July 14, 2010
On Wednesday, July 14, 2010 13:57:13 Heywood Floyd wrote:
> 
> Thank you for the elaborate answer!
> 
> When you put it like that, it does make sense. But I'm sorry. I refuse. The reason I refuse is those examples are void of any higher semantic meaning. Once we add a semantic meaning, it simply becomes backwards:
> 
> int[MAX_WIDTH][MAX_HEIGHT] map2d;
> map2d[x][y] = 9; // Wrong!
> 
> At least in my head, this is cognitive dissonance. To me, the language acts as if it's low-level semantics outweighs my high-level semantics and I should correct my thinking for that. I refuse! Seems to me it could just as well work as:
> 
> int[string][3] b;
> int[3] firstB = b["foo"];
> int i = firstB[0];
> int j = (b["foo"])[0];
> int k = b["foo"][0];
> 
> But I feel like I'm the only one feeling this, so I'll just let it go and hope my dear C-style arrays stay in :)
> 
> BR
> /HF
> 
> PS. Never thought I'd find a reason to love C.. DS.

Personally, I don't like it, but I also don't think that it necessarily makes sense to change it. From the point of view of how the compiler deduces types, it's exactly how it should work. The problem, of course, is that it doesn't match how we think. However, in order for us to be able to deduce complicated types, the compiler must be totally consistent in how it deduces the type. A prime example would be function pointers (particularly using the C syntax). To be able to pick it apart properly, you're going to have to understand how the compiler does it. The type is complicated enough that you're stuck. To read arrays in the reverse order that is done now, you'd have to read everything else in the reverse order to be consistent, which would really mean types like this:

[5](int) const a;

The whole thing is ugly. There's no question of that. But to "fix" it breaks other stuff. Types are read right to left. Being consistent with that makes it possible to understand more complicated types. The downside is that some simpler types become harder to understand. However, as long as you use dynamic arrays, this really isn't a problem.

int[][] a = new int[][](MAX_WIDTH, MAX_HEIGHT);

is totally clear and it works in the order that you think. The problem is when you use statically-allocated arrays. Personally, I'd advise you to just use dynamic arrays unless you do some profiling and find that a static array is better in a particular case. Doing things that way makes it so that the weird ordering in declarations isn't generally a problem.

I agree that this particular syntactic faux pas is a problem, but I don't think that anyone has come up with an appropriate solution. Simply reversing how it works now would make reading more complex types much harder. A more complex solution would likely be required. As it stands, it's a problem, but it's only a problem if you're declaring statically-allocated arrays. It's unpleasant, but it should be manageable.

- Jonathan M Davis
« First   ‹ Prev
1 2