What is the difference between a[x][y] and a[x,y]?

H. S. Teoh hsteoh at quickfur.ath.cx
Tue Jan 7 19:06:09 UTC 2020


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


More information about the Digitalmars-d-learn mailing list