Jump to page: 1 2
Thread overview
Multidimensional array
Jul 04, 2013
Oleksiy
Jul 04, 2013
Jonathan M Davis
Jul 05, 2013
monarch_dodra
Jul 05, 2013
Oleksiy
Jul 04, 2013
bearophile
Jul 05, 2013
Ali Çehreli
Jul 05, 2013
bearophile
Jul 05, 2013
Ali Çehreli
Jul 05, 2013
bearophile
Jul 05, 2013
monarch_dodra
Jul 05, 2013
monarch_dodra
Jul 06, 2013
Oleksiy
Jul 07, 2013
Ali Çehreli
Jul 06, 2013
TommiT
July 04, 2013
Hi,

I'm new to the language and would appreciate if anybody could clarify the following:

1. What is the rationale behind "prefix declaration" of an array? Using right-to-left order to declare an array and left-to-right order to access elements seems confusing.

2. Consider this code:
    dchar[3][5] arr = '.';
    arr[2][] = '!';
    writeln();
    writeln(arr);

Result: ["...", "...", "!!!", "...", "..."]
Which is expected. According to Ali Çehreli's tutorial, omitting the index of an array will result in operation being applied to the whole array (in this case element of another array).

change the code:
-   arr[2][] = '!';
+   arr[][2] = '!';

Still getting the same result: ["...", "...", "!!!", "...", "..."]

I would expect to get: ["..!", "..!", "..!", "..!", "..!"]
since omitted index would apply the operation to all elements of the first dimension of the array.

What am I missing?


Thanks,
Oleksiy
July 04, 2013
On Friday, July 05, 2013 00:39:47 Oleksiy wrote:
> Hi,
> 
> I'm new to the language and would appreciate if anybody could clarify the following:
> 
> 1. What is the rationale behind "prefix declaration" of an array? Using right-to-left order to declare an array and left-to-right order to access elements seems confusing.

Because the brackets are part of the type, and the type goes to the left of
the variable name. The fact that C put in on the right-hand side is actually
quite bizarre, but they also did nonsense like make it
so that in this declaration

int* p1, p2;

p1 is an int*, whereas p2 is an int, which D fixed (in D, both are int*).

Now, the downside to putting them on the left is the order of the sizes. Types are read outwards from variable name. This becomes particularly important when you try and understand stuff like C function pointer declarations, since the name ends up in the middle of the declaration. Take

int* i;

for example. It's a pointer to an int, not an int to a pointer. It's read right-to-left. Static array declarations are doing the same thing.

int[4][5] arr;

It's a static array of length 5 which holds static arrays of length 4 which hold ints. So, the ordering is completely consistent with how the rest of the type system works. Reading it from left-to-right would be inconsistent, much as most people tend to think of types that way.

Now, we've already broken that rule in it least one case - const and immutable. In C and C++,

const int* p;

and

int const* p;

are identical, and technically, the second one would be more correct - because it gives you pointer to a const int, not a pointer to an int const. But the first one is allowed and what is frequently used, if nothing else, because people tend to try and read type declarations left-to-right.

In D, however, we have

const int* p;

and

const(int*) p;

but no

int const* p;

So, in this case, we arguably already broke the right-to-left rule. As such, it arguably would have been better to also break that rule with regards to static arrays, but the decision on static arrays far predates even adding const into the language, so at the time static arrays were introduced, making them read left-to-right would have been inconsistent with everything else, and while const is now inconsistent with everything else, most everyone is used to writing const on the left anyway, so I doubt that much of anyone thought about it being inconsistent when it was added.

The whole "read the type outward from the variable name" deal is not as critical in D as it is in C/C++, because we don't declare function pointers in the same way (which is where it's truly critical in C/C++), but we inherited it from C/C++, and for the most part, D still follows it.

Fixing it so that the sizes for static arrays were read left-to-right would definitely be a usability improvement, but even if we were to decide that the whole "read the type outward from the variable name" was unimportant enough to make the change desirable, it would silently break all kinds of code at this point, so we're stuck with it.

- Jonathan M Davis
July 04, 2013
Oleksiy:

> 1. What is the rationale behind "prefix declaration" of an array? Using right-to-left order to declare an array and left-to-right order to access elements seems confusing.

I think the way Go language declares arrays and pointers is a bit better. But for the rationale of this part of D design others should answer you.


> 2. Consider this code:
>     dchar[3][5] arr = '.';
>     arr[2][] = '!';
>     writeln();
>     writeln(arr);
>
> Result: ["...", "...", "!!!", "...", "..."]
> Which is expected. According to Ali Çehreli's tutorial, omitting the index of an array will result in operation being applied to the whole array (in this case element of another array).
>
> change the code:
> -   arr[2][] = '!';
> +   arr[][2] = '!';
>
> Still getting the same result: ["...", "...", "!!!", "...", "..."]
>
> I would expect to get: ["..!", "..!", "..!", "..!", "..!"]
> since omitted index would apply the operation to all elements of the first dimension of the array.
>
> What am I missing?

Ali Çehreli's tutorial is not correct, or you have not understood it correctly.

In D there are dynamic arrays and fixed sized arrays.

When you write:

dchar[3][5] arr;

You are allocating a fixed sized matrix in place (often on the stack or you are defining one inside another class instance or struct instance).

The array-wise (vector operation) is done only one the last array, so this works:

arr[2][] = '!';

But:

arr[][2] = '!';

is a totally different thing. You are slicing the rows, and then you are assigning something to all the items of the third row.

Always compile your D code with "-wi", it gives you a warning here, helping you avoid your mistake. I am asking all the time to produce warnings on default, but Walkter&Andrei don't even answer "No" to my request.

Bye,
bearophile
July 05, 2013
On 07/04/2013 03:39 PM, Oleksiy wrote:

> 1. What is the rationale behind "prefix declaration" of an array? Using
> right-to-left order to declare an array and left-to-right order to
> access elements seems confusing.

It seems to be confusing to people who are used to C and C++'s inside-out definition. In D, it is always "type and then the square brackets".

> 2. Consider this code:
>      dchar[3][5] arr = '.';

That is an array of 5 elements where the type of each element is dchar[3].

However, that is a confusing syntax because the right-hand side is not the same type as the elements, which is dchar[3]. Perhaps D supports it for C compatibility?

It doesn't match the following. Here, the right-hand side is the same as the element type:

    int[2] arr2 = 42;
    assert(arr2 == [ 42, 42 ]);

But this doesn't compile:

    char[3][5] arr = [ '.', '.', '.' ];

Error: mismatched array lengths, 15 and 3

I see that as a bug but can't be sure.

>      arr[2][] = '!';

arr[2] is the third dchar[3] of arr. When [] is applied to it all of its elements are set to '!'.

>      writeln();
>      writeln(arr);
>
> Result: ["...", "...", "!!!", "...", "..."]

Looks correct.

> Which is expected. According to Ali Çehreli's tutorial, omitting the
> index of an array will result in operation being applied to the whole
> array (in this case element of another array).

The book either fails to mention the term "array-wise" or does not use it sufficiently but Ali Çehreli is correct in that arr[] means "all of the elements of arr".

> change the code:
> -   arr[2][] = '!';
> +   arr[][2] = '!';

arr[] is a slice to all of the elements of arr. When [2] is applied to it then you again get the third element.

> Still getting the same result: ["...", "...", "!!!", "...", "..."]

Still makes sense.

> I would expect to get: ["..!", "..!", "..!", "..!", "..!"]

Like C and C++, D does not have multi-dimensional arrays. You must do that yourself. One of the most straight-forward is by foreach:

import std.stdio;

void main()
{
    dchar[3][5] arr = '.';

    foreach (ref e; arr) {
        e[2] = '!';
    }

    writeln(arr);
}

["..!", "..!", "..!", "..!", "..!"]

> since omitted index would apply the operation to all elements of the
> first dimension of the array.

Sorry about that confusion but "a slice to all of the elements" and "array-wise operation" syntaxes are very similar.

  a[] alone is a slice to all of the elements of 'a'. When you use that syntax in an operation, then that operation is applied "array-wise":

  a[] = b[] + c[];

That is the same as

  a[i] = b[i] + c[i]

for all valid values of 'i'.

Ali

July 05, 2013
Ali Çehreli:

> However, that is a confusing syntax because the right-hand side is not the same type as the elements, which is dchar[3]. Perhaps D supports it for C compatibility?
>
> It doesn't match the following. Here, the right-hand side is the same as the element type:
>
>     int[2] arr2 = 42;
>     assert(arr2 == [ 42, 42 ]);
>
> But this doesn't compile:
>
>     char[3][5] arr = [ '.', '.', '.' ];
>
> Error: mismatched array lengths, 15 and 3
>
> I see that as a bug but can't be sure.

In D char literals as 'x' or even string literals as "xx" are seen as instances of all the types of strings and chars, it's not a bug, it's a feature:

void main() {
    char x1  = 'x';
    wchar x2 = 'x';
    dchar x3 = 'x';
    string s1  = "xx";
    wstring s2 = "xx";
    dstring s3 = "xx";
}


There is a way to specify the type of a string, so this gives errors:

void main() {
    string s1  = "xx"d;
    string s2  = "xx"w;
    wstring s3 = "xx"d;
}

Bye,
bearophile
July 05, 2013
On 07/04/2013 06:43 PM, bearophile wrote:

> Ali Çehreli:
>
>> However, that is a confusing syntax because the right-hand side is not
>> the same type as the elements, which is dchar[3]. Perhaps D supports
>> it for C compatibility?
>>
>> It doesn't match the following. Here, the right-hand side is the same
>> as the element type:
>>
>>     int[2] arr2 = 42;
>>     assert(arr2 == [ 42, 42 ]);
>>
>> But this doesn't compile:
>>
>>     char[3][5] arr = [ '.', '.', '.' ];
>>
>> Error: mismatched array lengths, 15 and 3
>>
>> I see that as a bug but can't be sure.
>
> In D char literals as 'x' or even string literals as "xx" are seen as
> instances of all the types of strings and chars, it's not a bug, it's a
> feature:
>
> void main() {
>      char x1  = 'x';
>      wchar x2 = 'x';
>      dchar x3 = 'x';
>      string s1  = "xx";
>      wstring s2 = "xx";
>      dstring s3 = "xx";
> }
>
>
> There is a way to specify the type of a string, so this gives errors:
>
> void main() {
>      string s1  = "xx"d;
>      string s2  = "xx"w;
>      wstring s3 = "xx"d;
> }
>
> Bye,
> bearophile

Thanks for the refresher. My comment was about array initialization. We can define an array by a value on the right-hand side that is the same as the element types:

    int[2] arr2 = 42;
    assert(arr2 == [ 42, 42 ]);

Good: Every element has the value 42.

Now I have another array where the elements are of type int[3]:

    int[3][2] arr = [ 10, 20, 30 ];

Error: mismatched array lengths, 6 and 3

Do you see the inconsistency? The element type is int[3] and the initial value is [ 10, 20, 30 ]. However there is a compilation error.

Further, the code compiles even if I use a different type as the initial value:

    int[3][2] arr = 10;

I would expect that to be an error because the element type is not an int but int[3]. We know that an int[3] can be initialized by 10 but a 10 should not be allowed to be used as an int[3].

That was my point.

Ali

July 05, 2013
Ali Çehreli:

>     int[3][2] arr = 10;
>
> I would expect that to be an error because the element type is not an int but int[3]. We know that an int[3] can be initialized by 10 but a 10 should not be allowed to be used as an int[3].
>
> That was my point.

I see. That's another feature :-)

Bye,
bearophile
July 05, 2013
On Thursday, 4 July 2013 at 23:02:10 UTC, Jonathan M Davis wrote:
> On Friday, July 05, 2013 00:39:47 Oleksiy wrote:
>> Hi,
>> 
>> I'm new to the language and would appreciate if anybody could
>> clarify the following:
>> 
>> 1. What is the rationale behind "prefix declaration" of an array?
>> Using right-to-left order to declare an array and left-to-right
>> order to access elements seems confusing.
>
> Because the brackets are part of the type, and the type goes to the left of
> the variable name. The fact that C put in on the right-hand side is actually
> quite bizarre, but they also did nonsense like make it
> so that in this declaration
>
> int* p1, p2;
>
> p1 is an int*, whereas p2 is an int, which D fixed (in D, both are int*).
>
> - Jonathan M Davis

Arguably, this all comes from C's "value centric" scheme. When you write (not the C-like placement of the *):

int *p, a;

It means: with *p and a, you get an int.

Ditto, when you write:
int arr[5];
It is written that way to reflect that usage is:
*p = arr[0];

As stated in the first question, it makes sense that way in the context of:
#define WIDTH = 10
#define HEIGHT = 5
int matrix[WIDTH][HEIGHT];
a = matrix[w][h];

I'm just saying that's the explanation for it. Once you have to start making the distinction between "array of pointers" and "pointer to array", then clearly, things start crumbling apart, and D's approach is simply better. I'm just saying, there *is* an historical reason for this, not just arbitrary stupidness.

The fact that in D, declaration and indexing is "reversed" is something I've truly never even noticed, since to me, naturally, declarations read right to left, then parsing is left to right. So no problem there.
July 05, 2013
On Friday, 5 July 2013 at 01:43:45 UTC, bearophile wrote:
> There is a way to specify the type of a string, so this gives errors:
>
> void main() {
>     string s1  = "xx"d;
>     string s2  = "xx"w;
>     wstring s3 = "xx"d;
> }
>
> Bye,
> bearophile

Also, don't forget the often forgotten explicit c suffix:
dstring s = "hello"c; //Error: cannot implicitly convert
expression ("hello"c) of type string to immutable(dchar)[]
July 05, 2013
On Friday, 5 July 2013 at 01:53:22 UTC, Ali Çehreli wrote:
> Now I have another array where the elements are of type int[3]:
>
>     int[3][2] arr = [ 10, 20, 30 ];
>
> Error: mismatched array lengths, 6 and 3
>
> Ali

Combined with the fact that *this* works:

int[3][2] arr = [ 10, 20, 30, 10, 20, 30 ];

Seems like a bug to me. I'd file it, personally.
« First   ‹ Prev
1 2