www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Doubt - Static multidimension arrays

reply albert <no email.com> writes:
Hello,

I was looking over 
http://dlang.org/spec/arrays.html#rectangular-arrays:

And I've found that D multidimensional arrays are Rectangular, so 
it's impossible to create non-rectangular multidimensional static 
array like:

int[2][5] arr;

?
Jan 18
parent reply cym13 <cpicard openmailbox.org> writes:
On Tuesday, 19 January 2016 at 02:47:04 UTC, albert wrote:
 Hello,

 I was looking over 
 http://dlang.org/spec/arrays.html#rectangular-arrays:

 And I've found that D multidimensional arrays are Rectangular, 
 so it's impossible to create non-rectangular multidimensional 
 static array like:

 int[2][5] arr;

 ?
How is that not rectangular? It's sounds like you're confusing it with "square".
Jan 18
parent reply albert00 <no mail.com> writes:
On Tuesday, 19 January 2016 at 02:54:03 UTC, cym13 wrote:
 int[2][5] arr;

 ?
How is that not rectangular? It's sounds like you're confusing it with "square".
Ow my problem is: int[2][2] arr; // This works int[2][5] arr; // This not working And I'd like to create the former.
Jan 18
parent reply albert00 <no mail.com> writes:
Well maybe it was my fault, but anyway, here's a small example of 
what I was working on:

void main(){
	// Array 1	
	int[2][2] arr1;

	arr1[0][0] = 1;
	arr1[0][1] = 2;
	arr1[1][0] = 3;
	arr1[1][1] = 4;

	// Array 2
	int[1][2] arr2;
	
	//arr2[0][0] = 1;
	//arr2[0][1] = 2; // <- Error
	
	arr2[0][0] = 1;
	arr2[1][0] = 2;
}

So for what I can see on Array 2 is the column/row works 
otherwise.

It's strange since I was declaring int[1][2] (1 row / 2 columns) 
and then accessing as:

	arr2[0][0] = 1;
	arr2[1][0] = 2;

Seems like 2 rows and 1 column. This makes sense?
Jan 18
next sibling parent reply tsbockman <thomas.bockman gmail.com> writes:
On Tuesday, 19 January 2016 at 03:20:30 UTC, albert00 wrote:
 [...]
You're not really creating a rectangular array - what you're making is an array *of arrays*: int[10][5] a; // An array of 5 (int[10]) writeln(typeof(a).stringof); // int[10][5] writeln(typeof(a[4]).stringof); // int[10] writeln(typeof(a[4][9]).stringof); // int The recently accepted `std.experimental.ndslice` module provides a real multi-dimensional array interface: http://dlang.org/phobos-prerelease/std_experimental_ndslice.html (There might be something else relevant in Phobos as well, with a less numerically-oriented focus; I'm not sure.)
Jan 18
parent reply albert00 <no email.com> writes:
On Tuesday, 19 January 2016 at 04:50:18 UTC, tsbockman wrote:
 On Tuesday, 19 January 2016 at 03:20:30 UTC, albert00 wrote:
 [...]
... what you're making is an array *of arrays*:
Maybe I was misunderstood, because in fact that is what I was making an array of arrays, but my problem in fact was in accessing it. Like I said above: I was declaring int[1][2] arr: But to access I need to invert the columns like: arr2[0][0] = 1; arr2[1][0] = 2;
 int[10][5] a; // An array of 5 (int[10])
Using your example, in my head I'd access the last element as: a[9][4] but that would give me an error: Error: array index 9 is out of bounds arr[0 .. 5] So I need to invert a[4][9]. Again seems a bit strange "FOR ME" since I declare in one way and access the other way. albert. PS: I'm changing my name because I think there is another user with the same name, that Icon is not mine.
Jan 18
next sibling parent reply tsbockman <thomas.bockman gmail.com> writes:
On Tuesday, 19 January 2016 at 07:21:39 UTC, albert00 wrote:
 Again seems a bit strange "FOR ME" since I declare in one way 
 and access the other way.

 albert.
That's because you're stuck in the mindset that 2d arrays are somehow *special*. If I do this: Row[5] a; const b = a[4]; Of what type do you expect `b` to be? Of type `Row`, yes? Now let's define `Row`: alias Row = int[10]; Row[5] a; const b = a[9]; const int = c = b[4]; Of what type is `b` now? Of course it is still `Row`. By substitution, we expect `b[0]` to be equal to `(a[9])[4]`.
Jan 18
next sibling parent reply tsbockman <thomas.bockman gmail.com> writes:
On Tuesday, 19 January 2016 at 07:32:22 UTC, tsbockman wrote:
 Now let's define `Row`:

 alias Row = int[10];
 Row[5] a;
 const b = a[9];
 const int = c = b[4];

 Of what type is `b` now? Of course it is still `Row`.

 By substitution, we expect `b[0]` to be equal to `(a[9])[4]`.
Sigh. I should proof-read my stuff more carefully: alias Row = int[10]; Row[5] a; const Row b = a[4]; const int = c = b[9]; Of what type is `b` now? Of course it is still `Row`. By substitution, we expect `b[0]` to be equal to `(a[4])[9]`.
Jan 18
parent tsbockman <thomas.bockman gmail.com> writes:
On Tuesday, 19 January 2016 at 07:35:34 UTC, tsbockman wrote:
 By substitution, we expect `b[0]` to be equal to `(a[4])[9]`.
Apparently I need to get more sleep: By substitution, we expect `b[9]` to be equal to `(a[4])[9]`.
Jan 18
prev sibling parent reply Mike Parker <aldacron gmail.com> writes:
On Tuesday, 19 January 2016 at 07:32:22 UTC, tsbockman wrote:
 That's because you're stuck in the mindset that 2d arrays are 
 somehow *special*. If I do this:
It's not that he's seeing them as special, it's just that indexing them in D is different than doing so in C or C++. It trips a lot of people up.
Jan 18
parent reply tsbockman <thomas.bockman gmail.com> writes:
On Tuesday, 19 January 2016 at 07:46:59 UTC, Mike Parker wrote:
 It's not that he's seeing them as special, it's just that 
 indexing them in D is different than doing so in C or C++. It 
 trips a lot of people up.
No, the difference is actually in C/C++ 's declaration syntax; the way that indexing works is exactly the same as in D. This C++ code (http://codepad.org/XeVSndBP): #include <iostream> #include <cstring> class Row { int data[10]; public: int& operator[](int x) { return data[x]; } }; int main(void) { int arr2d[5][10]; Row arrOfArrs[5]; for(int r = 0; r < 5; ++r) { for(int c = 0; c < 10; ++c) { arr2d[r][c] = (r * 10) + c; arrOfArrs[r][c] = (r * 10) + c; } } cout << (arr2d[4][9] == arrOfArrs[4][9]) << endl; cout << (memcmp(arr2d, arrOfArrs, sizeof(int)*5*10) == 0) << endl; return 0; } Does exactly the same thing as this D code (http://dpaste.dzfl.pl/1731eb86bc83): import std.stdio; import core.stdc.string; alias Row = int[10]; int main() { int[10][5] arr2d; Row[5] arrOfArrs; for(int r = 0; r < 5; ++r) { for(int c = 0; c < 10; ++c) { arr2d[r][c] = (r * 10) + c; arrOfArrs[r][c] = (r * 10) + c; } } writeln(arr2d[4][9] == arrOfArrs[4][9]); writeln(memcmp(arr2d.ptr, arrOfArrs.ptr, int.sizeof*5*10) == 0); return 0; } The only relevant difference between the two, is that the order of the row and column specification is swapped in *the declaration*, not when indexing.
Jan 19
parent reply Mike Parker <aldacron gmail.com> writes:
On Tuesday, 19 January 2016 at 08:27:56 UTC, tsbockman wrote:

 The only relevant difference between the two, is that the order 
 of the row and column specification is swapped in *the 
 declaration*, not when indexing.
Newcomers to D tend to think in terms of C when they declare arrays, so the confusion comes when they find out they have to index it in a way that is the reverse of what they expect. Yes, it's because the declaration syntax is different, but it's always the indexing that trips them up. That's all I meant. It usually isn't obvious what the root of the misunderstanding is until someone explains it.
Jan 19
parent reply tsbockman <thomas.bockman gmail.com> writes:
On Tuesday, 19 January 2016 at 13:00:19 UTC, Mike Parker wrote:
 Newcomers to D tend to think in terms of C when they declare 
 arrays, so the confusion comes when they find out they have to 
 index it in a way that is the reverse of what they expect. Yes, 
 it's because the declaration syntax is different, but it's 
 always the indexing that trips them up. That's all I meant. It 
 usually isn't obvious what the root of the misunderstanding is 
 until someone explains it.
I guess my problem is that I'm assuming that people who've worked with C++ will understand that this: `int arr[5][10]` is *not* an "array of arrays"; my point would be clearer if I'd written this: template<class T, int size> class Array { T data[size]; public: T& operator[](int x) { return data[x]; } }; int arr2d[5][10]; Array< Array< int, 10 >, 5 > arrOfArrs; Notice that an actual "array of arrays" declaration has the dimensions listed in the same order as D. I'd hope that expressing it this way would make it obvious to any experienced C/C++ programmer why D's declaration syntax is the way that it is. Anyway, I'll give it a rest now. I thought this way of looking at it would make things easier to understand, but I guess not...
Jan 19
parent Mike Parker <aldacron gmail.com> writes:
On Tuesday, 19 January 2016 at 14:58:51 UTC, tsbockman wrote:

 Anyway, I'll give it a rest now. I thought this way of looking 
 at it would make things easier to understand, but I guess not...
In my experience, it's focusing on the types in the D array syntax, rather than the actual ordering, that helps people over the hump. The rows vs. columns bit is useful to demonstrate the difference, but the focus on types clarifies it. That's how I approached it in the section on arrays in Learning D.
Jan 19
prev sibling parent reply Mike Parker <aldacron gmail.com> writes:
On Tuesday, 19 January 2016 at 07:21:39 UTC, albert00 wrote:
 On Tuesday, 19 January 2016 at 04:50:18 UTC, tsbockman wrote:
 On Tuesday, 19 January 2016 at 03:20:30 UTC, albert00 wrote:
 [...]
... what you're making is an array *of arrays*:
Maybe I was misunderstood, because in fact that is what I was making an array of arrays, but my problem in fact was in accessing it. Like I said above: I was declaring int[1][2] arr: But to access I need to invert the columns like: arr2[0][0] = 1; arr2[1][0] = 2;
 int[10][5] a; // An array of 5 (int[10])
Using your example, in my head I'd access the last element as: a[9][4] but that would give me an error: Error: array index 9 is out of bounds arr[0 .. 5] So I need to invert a[4][9]. Again seems a bit strange "FOR ME" since I declare in one way and access the other way.
Try not to think in terms of columns and rows. It makes more sense to think of it in terms of *types*. Given the following array declaration, which is an array of int: int[2] nums; Then any index into nums (such as nums[0]) is going to return an int. The same logic applies given this array declaration, which is an array of int[2]: int[2][5] numArrays; Then any index into numArrays is going to return int[2] (for example, numArrays[0]). In order to access the members of the returned array, we need one more index, so that int[0][1] is the second element of the first array. In other words, it's shorthand for: int[2] arr = numArrays[0]; int num = arr[1]; Seen in this light, it is entirely consistent and not strange at all. It just takes some people a bit of effort to stop thinking in terms of C's multidimensional arrays so that it becomes second-nature to see it in this light. I was using D for quite some time before I finally got used to it.
Jan 18
parent reply alb <a nomail.com> writes:
So guys: Ali, Mike Parker and tsbockman thanks for all your 
explanation, in fact looking now I and after making some tests I 
really got it.

So:
    int[2]    a1; // Array of 2 elements of type int

    int[2][5] a2; // Array of 2 elements of type int divided in 5 
rows

    writeln(a2[0]); // = accessing row 0 = [0,0]
    writeln(a2[4]); // = accessing row 4 = [0,0]

If that in mind, now it all makes sense for me, and of course 
it's consistent as well. Sorry to bother about this, but I think 
this will help other newcomers.

Thanks again for help/tips which helped turn my mindset.

Albert.
Jan 19
parent reply tsbockman <thomas.bockman gmail.com> writes:
On Tuesday, 19 January 2016 at 19:14:30 UTC, alb wrote:
 So guys: Ali, Mike Parker and tsbockman thanks for all your 
 explanation, in fact looking now I and after making some tests 
 I really got it.

 So:
    int[2]    a1; // Array of 2 elements of type int

    int[2][5] a2; // Array of 2 elements of type int divided in 
 5 rows

    writeln(a2[0]); // = accessing row 0 = [0,0]
    writeln(a2[4]); // = accessing row 4 = [0,0]
One other thing you may want to keep in mind when working on this kind of thing - when you loop over a multi-dimensional array, the order matters. For large arrays, this: int[c_max][r_max] arr; foreach(r; 0 .. r_max) { foreach(c; 0 .. c_max) { // do something with arr[r][c] here } } Can be *much* faster than this: int[c_max][r_max] arr; foreach(c; 0 .. c_max) { foreach(r; 0 .. r_max) { // do something with arr[r][c] here } } The reason is that the first version access the elements in the order that they are actually stored in memory, whereas the second forces the CPU to jump between rows for each element.
 If that in mind, now it all makes sense for me, and of course 
 it's consistent as well. Sorry to bother about this, but I 
 think this will help other newcomers.

 Thanks again for help/tips which helped turn my mindset.

 Albert.
You're welcome. And yes, it can definitely be confusing. I understand why the array syntax in D is the way it is, but that still doesn't always save me from mixing things up once in a while anyway.
Jan 19
parent reply Nemo <notmyemailaddress gmail.com> writes:
On Tuesday, 19 January 2016 at 20:39:37 UTC, tsbockman wrote:
 On Tuesday, 19 January 2016 at 19:14:30 UTC, alb wrote:
    [...]
One other thing you may want to keep in mind when working on this kind of thing - when you loop over a multi-dimensional array, the order matters. For large arrays, this: int[c_max][r_max] arr; foreach(r; 0 .. r_max) { foreach(c; 0 .. c_max) { // do something with arr[r][c] here } } Can be *much* faster than this: int[c_max][r_max] arr; foreach(c; 0 .. c_max) { foreach(r; 0 .. r_max) { // do something with arr[r][c] here } } The reason is that the first version access the elements in the order that they are actually stored in memory, whereas the second forces the CPU to jump between rows for each element.
 [...]
You're welcome. And yes, it can definitely be confusing. I understand why the array syntax in D is the way it is, but that still doesn't always save me from mixing things up once in a while anyway.
I don't remember where I saw it, but actually, in static multi-dimensional arrays, arr[0][1] is next to arr[0][0] in memory. You can see it with: void main () { ubyte [7][5] arr; import std.stdio : writeln; writeln ( & arr[0][0], " ", & arr[0][1], " ", & arr [1][0] ); }
Jan 20
parent tsbockman <thomas.bockman gmail.com> writes:
On Thursday, 21 January 2016 at 01:36:21 UTC, Nemo wrote:
 I don't remember where I saw it, but actually, in static 
 multi-dimensional arrays, arr[0][1] is next to arr[0][0] in 
 memory. You can see it with:

 void main () {
   ubyte [7][5] arr;
   import std.stdio : writeln;
   writeln ( & arr[0][0], " ", & arr[0][1], " ", & arr [1][0] );
 }
Yes. That's consistent both with what I wrote in the message that you quoted, and with how non-static arrays work. dpaste: http://dpaste.dzfl.pl/5ca02bd98f82
Jan 21
prev sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 01/18/2016 07:20 PM, albert00 wrote:

 It's strange since I was declaring int[1][2] (1 row / 2 columns) and
 then accessing as:

      arr2[0][0] = 1;
      arr2[1][0] = 2;

 Seems like 2 rows and 1 column. This makes sense?
Yes, it makes sense and its consistent. This is one of many little things that D is an improvement over C and C++. Static array type syntax is always the following: Type[length] For example, the following is a row with two columns: int[2] When you want a certain number of those, you follow the same array definition syntax: Type[length] Since each element is int[2] in this case, for 3 rows we get: int[2][3] So, in order to get 1 row of 2 columns, you would write int[2][1] Accessing elements is consistent as well: var[index] So, arr2[0] would be the first row, which has two columns; which is further accessed by another index: arr2[0][1] is first row, second column. Ali
Jan 18
parent reply Albert00 <no email.com> writes:
On Tuesday, 19 January 2016 at 05:32:07 UTC, Ali Çehreli wrote:

Ali, look what you said:

 For example, the following is a row with two columns:

     int[2]
Then you said:
 So, in order to get 1 row of 2 columns, you would write

     int[2][1]
So the first pair of square-brackets is the column and second is the row as you said above, but look what happens when I try to access thinking that way: void main(){ int[2][1] arr; // 2 columns & 1 row as Ali said... arr[0][0] = 1; arr[1][0] = 2; } ERROR: /d609/f167.d(14): Error: array index 1 is out of bounds arr[0 .. 1] /d609/f167.d(14): Error: array index 1 is out of bounds arr[0 .. 1] So now the first pair of brackets in fact is the ROW and the second is the COLUMN, because this works: void main(){ int[2][1] arr; // 2 columns & 1 row arr[0][0] = 1; arr[0][1] = 2; } Maybe I'm really dumb, but you need to agree that even with your good explanation it still doesn't making sense. Albert.
Jan 18
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 01/18/2016 11:12 PM, Albert00 wrote:
 On Tuesday, 19 January 2016 at 05:32:07 UTC, Ali Çehreli wrote:

 Ali, look what you said:

 For example, the following is a row with two columns:

     int[2]
Then you said:
 So, in order to get 1 row of 2 columns, you would write

     int[2][1]
To rephrase myself, that is an array of 1 element, where the element type is int[2]. So the only valid index is 0, which gives you an element of int[2]. (You can further index into that element of course.)
 So the first pair of square-brackets is the column and second is the row
 as you said above,
I stress the fact that it is always the following syntax: Type[length] So, again, if we have an array of 1-element where the elements are of type int[2], then it is this (space added for readability): int[2] [1]
 but look what happens when I try to access thinking
 that way:
I suspect C and C++ way for inside-out (or is it outside-in) syntax is affecting your thinking. ;)
 void main(){
      int[2][1] arr; // 2 columns & 1 row as Ali said...

      arr[0]
That one gives you the first element.
 [0] = 1;
and that one gives you the first element of that first element.
      arr[1][0] = 2;
Sorry, there is no element-1 for the arr: That has only one element.
 }

 ERROR:

 /d609/f167.d(14): Error: array index 1 is out of bounds arr[0 .. 1]
 /d609/f167.d(14): Error: array index 1 is out of bounds arr[0 .. 1]


 So now the first pair of brackets in fact is the ROW and the second is
 the COLUMN, because this works:

 void main(){
      int[2][1] arr; // 2 columns & 1 row
Yes, that's exactly what I said. :)
      arr[0][0] = 1;
      arr[0][1] = 2;
 }

 Maybe I'm really dumb,
Not at all. I blame C and C++. ;)
 but you need to agree that even with your good explanation it
 still doesn't making sense.
I don't agree: It makes sense and is consistent. :)
 Albert.
Ali
Jan 18
parent alb <aa ee.com> writes:
On Tuesday, 19 January 2016 at 07:19:54 UTC, Ali Çehreli wrote:
 ...
Well anyway thanks for your help. For now I'll just think the otherwise. :) Albert.
Jan 18