www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - What is the difference between a[x][y] and a[x,y]?

reply =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
I read all the docs but I'm not toally sure. Is it that [x][y] is a 2D 
array-index, where as [x,y] is a slice?

But the example in docs for opIndexAssign uses the [x,y] syntax, which 
is confusing:

```
struct A
{
    int opIndexAssign(int value, size_t i1, size_t i2);
}

void test()
{
    A a;
    a[i,3] = 7;  // same as a.opIndexAssign(7,i,3);
}
```

And what is the difference between opIndexAssign and opIndexOpAssign?

Background: I have a class with a 2D array of some other class objects 
as memember and want to be able to use it as LHS and RHS.

-- 
Robert M. Münch
http://www.saphirion.com
smarter | better | faster
Jan 07 2020
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 7 January 2020 at 17:38:59 UTC, Robert M. Münch wrote:
 I read all the docs but I'm not toally sure. Is it that [x][y] 
 is a 2D array-index, where as [x,y] is a slice?
So [x][y] indexes an array of arrays. [x,y] indexes a single array that has two dimensions. This can be kinda confusing because we often think of int[4][4] as being a 2d array, but the D language actually technically sees that as an array of arrays. So it is indexed with [x][y]. There is no built in multi-dimensional array, it is only syntax provided for library types to implement with the opIndexAssign overloads.
 And what is the difference between opIndexAssign and 
 opIndexOpAssign?
foo[5] = x; // opIndexAssign foo[4] += x; // opIndexOpAssign So the operator there like += or -= or *= etc become opAssign, whereas plain = is just plain Assign.
Jan 07 2020
parent =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
On 2020-01-07 17:42:48 +0000, Adam D. Ruppe said:

 So [x][y] indexes an array of arrays.
Yes, that's what I understand. And both can be dynamic, so that I can have a "flattering" layout where not all arrays have the same length.
  [x,y] indexes a single array that has two dimensions.
Does this fix the dimension sizes? So I can't add a "row" or "column" at runtime? What are the difference use-cases for these two? For example, I'm doing a grid widget but want to add/remove rows/columns. Can this only be done with a array-of-array? Is the memory continuous in the [x,y] case, whereas in the [x][y] this is not necessarily the case?
 This can be kinda confusing because we often think of
 
 int[4][4]
 
 as being a 2d array, but the D language actually technically sees that 
 as an array of arrays. So it is indexed with [x][y].
Yes, it's confusing as I don't understand the [x,y] case :-)
 There is no built in multi-dimensional array, it is only syntax 
 provided for library types to implement with the opIndexAssign 
 overloads.
I don't get that sentence. Above you write "a single array that has two dimensions" which IMO is exactly a multi-dimensional array.
 So the operator there like += or -= or *= etc become opAssign, whereas 
 plain = is just plain Assign.
Got it. Thanks. -- Robert M. Münch http://www.saphirion.com smarter | better | faster
Jan 07 2020
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Jan 07, 2020 at 06:38:59PM +0100, Robert M. Münch via
Digitalmars-d-learn wrote:
 I read all the docs but I'm not toally sure. Is it that [x][y] is a 2D
 array-index, where as [x,y] is a slice?
arr[x][y] is indexing an array of arrays. arr[x,y] is indexing an actual multi-dimensional array. The language does not have a built-in implementation of multi-dimensional arrays, so the only time you can use the arr[x,y] syntax is when you write your own type that overloads opIndex. Here's a quick-n-dirty example: struct Array2D(T) { private T[] impl; private int width, height; this(int w, int h) { width = w; height = h; impl.length = width*height; } // Notice that opIndex takes *two* parameters, this is // what allows us to use arr[x,y] syntax. ref T opIndex(int i, int j) { return impl[i + width*j]; } } auto arr = Array2D!int(3, 3); arr[0, 0] = 1; arr[1, 1] = 2; ... In the same vein you can write a type that declares opIndex with 3 or more parameters, then you can get higher-dimensional indexing. If you implement opDollar, then you can also use $ in your indexing, e.g.: struct MyArray(T) { private int width, height; ... int opSlice(size_t dim)() { static if (dim == 0) return width; else return height; } } MyArray!int arr; arr[$-2, $-3] = 1; You can also implement slicing syntax like: arr[2 .. $, 0 .. 3] To do this, you need to overload .opSlice with two indices, returning some object defining a range (i.e., encapsulates "2..$" or "0..3"), say we call it IdxRange. Then overload opIndex to understand arguments of type IdxRange. For example: struct IdxRange { int start, end; } struct MyArray(T) { ... IdxRange opSlice(int start, int end) { return IdxRange(start,end); } T opIndex(IdxRange colRange, IdxRange rowRange) { ... // return subarray here } } Basically, this: arr[2 .. $, 0 .. 3] gets translated to this: arr.opIndex(arr.opSlice(2, arr.opDollar!0), arr.opSlice(0, 3)); It's up to you how to implement all of this, of course. The language itself doesn't ship a built-in type that implements this, but it does provide the scaffolding for you to build a custom multi-dimensional array type. [...]
 And what is the difference between opIndexAssign and opIndexOpAssign?
[...] opIndexAssign is for implementing operations of the form: arr[x, y] = z; opIndexOpAssign is for implementing operations of the form: arr[x, y] += z; arr[x, y] *= z; // etc. In the previous examples I had opIndex return ref, for simplicity, so you could just assign to the array element via the ref. But if your array implementation differentiates between looking up an element vs. assigning to an element, then you'll want to provide your own implementation of opIndexAssign and/or opIndexOpAssign: struct MyArray(T) { ... void opIndexAssign(T value, int x, int y) { T* elem = lookupElement(x, y); *elem = value; } void opIndexOpAssign(string op)(T value, int x, int y) { T* elem = lookupElement(x, y); mixin("*elem "~op~"= value;"); } } T -- Try to keep an open mind, but not so open your brain falls out. -- theboz
Jan 07 2020
parent reply =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
On 2020-01-07 19:06:09 +0000, H. S. Teoh said:

 It's up to you how to implement all of this, of course. The language
 itself doesn't ship a built-in type that implements this, but it does
 provide the scaffolding for you to build a custom multi-dimensional
 array type.
Hi, thanks for your extensive answer! Helped a lot... And the above paraghraph brings it to the core. How can this be added to the D docs? I think adding such clear "this is the idea how you should use it" intros would help a lot to see the world from a d-ish perspective. -- Robert M. Münch http://www.saphirion.com smarter | better | faster
Jan 08 2020
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jan 08, 2020 at 09:09:23AM +0100, Robert M. Münch via
Digitalmars-d-learn wrote:
 On 2020-01-07 19:06:09 +0000, H. S. Teoh said:
 
 It's up to you how to implement all of this, of course. The language
 itself doesn't ship a built-in type that implements this, but it
 does provide the scaffolding for you to build a custom
 multi-dimensional array type.
Hi, thanks for your extensive answer! Helped a lot... And the above paraghraph brings it to the core. How can this be added to the D docs? I think adding such clear "this is the idea how you should use it" intros would help a lot to see the world from a d-ish perspective.
[...] File a bug against dlang.org, and maybe when I get some free time I'll try to write something up. T -- Designer clothes: how to cover less by paying more.
Jan 08 2020