Introduction to multidimensional arrays in D

Author: Dr Chibisi Chima-Okereke Created: September 19, 2020 12:10:22 GMT Published: September 19, 2020 12:10:22 GMT

Introduction

In a similar way to C and C++ where one dimensional arrays are a block of memory traversed by a pointer, in D, arrays are defined by a block of memory, a pointer, but they also have a length which make them easier to safely traverse. In languages such as C, C++, and D, the arrays that are built into the language are not the same as arrays in languages such as R, Numpy, or Julia, which are composed of a single block of memory addressed in different ways to create the effect of being multidimensional arrays. In the aforementioned native languages, multidimensional arrays are effectively nested arrays, for instance in D, a two dimensional array is an array where each item is itself, an array of elements (though packages like Mir implement single memory block multidimensional arrays, and Julia differentiates nested from multidimensional arrays). There is another article on multidimensional arrays in D located here.

In this article, we outline how to create and iterate over static and dynamic multidimensional arrays in D, and how to automatically create them using string mixins.

Creating static and dynamic arrays in one dimension

Creating and working with one dimensional static arrays

Static arrays are arrays where the dimensions are constants, that is once set they can not be changed. Static arrays can be created at runtime, and their contents are mutable unless marked otherwise. A static array is declared as:

int[4] sarr1d = [1, 2, 3, 4];
writeln("Static array: ", sarr1d);

output:

Static array: [1, 2, 3, 4]

We can assign values in a static array as:

sarr1d[0] = 0;
writeln("assignment: ", sarr1d);

output:

assignment: [0, 2, 3, 4]

carry out slice assignment as:

sarr1d[1..$] = [1, 2, 3];
writeln("slice assignment: ", sarr1d);

single value to multiple position in a slice:

sarr1d[] = 0;
writeln("single slice assignment: ", sarr1d);

output

single slice assignment: [0, 0, 0, 0]

we can iterative over any array like this:

foreach(i; 0..sarr1d.length)
{
  sarr1d[i] = i*i;
}
writeln("Iterating over the array: ", sarr1d);

output:

Iterating over the array: [0, 1, 4, 9]

and we can use a regular for loop also:

for(int i = 0; i < sarr1d.length; ++i)
  sarr1d[i] = -i;
writeln("after for loop: ", sarr1d);

output:

After for loop: [0, -1, -2, -3]

Creating and working with one dimensional dynamic arrays

The difference between static and dynamic arrays is that the dimensions of dynamic arrays are not constant and can be changed at anytime.

We can create a dynamic array:

int[] darr1d = [5, 6, 7, 8];
writeln("Dynamic array: ", darr1d);

output:

Dynamic array: [5, 6, 7, 8]

Appending items to the array:

darr1d = 4 ~ darr1d ~ 9;
writeln("Appended array: ", darr1d);

output:

Appended array: [4, 5, 6, 7, 8, 9]

changing the length of the array:

darr1d.length = 4;
writeln("Changed array length: ", darr1d);

output:

Changed array length: [4, 5, 6, 7]

We can also create a dynamic array like this:

darr1d = new int[](4);

or this:

darr1d = new int[4];

Creating static and dynamic arrays in two dimensions

Static two dimensional arrays

Two dimensional static arrays:

int[3][4] sarr2d = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]];
writeln("2D static arrays: ", sarr2d);

output:

2D static arrays: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]

The lengths of the array and sub-array is:

writeln("Array length: ", sarr2d.length);
writeln("Sub-array length: ", sarr2d[0].length);

output:

Array length: 4
Sub-array length: 3

notice that the declaration of the static array type is int[3][4], and the dimension of the inner-most array is first and the length of the outermost array is last.

Partially static two dimensional arrays

We can create two dimensional arrays where the innermost arrays have a static length but the outer arrays are dynamic:

int[3][] sarr2d2 = new int[3][](4);
writeln("sarr2d2: ", sarr2d2);

output:

sarr2d2: [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]

Dynamic two dimensional arrays

Here, we create a dynamic two dimensional array:

int[][] sarr2d3 = new int[][](4, 3);
writeln("sarr2d3: ", sarr2d3);

output:

sarr2d3: [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]

notice how the declaration is now reversed. For the static array, we used int[3][4], now we use new int[][](4, 3) to create the same array dimensions. A dynamic array like this can be ragged:

sarr2d3 ~= [0, 0, 0, 0];
writeln(sarr2d3);

Attached a different sized sub-array:

[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0, 0]]

Creating multidimensional arrays using string mixins

This section outlines how to create a static or dynamic multidimensional array simply by passing the dimensions into a function either as an array or as a variable number of arguments. Using the mathematical convention for arrays, we can write a function that creates static array in the form T[nk]...[n1][n0] x; like this:

string createStaticArray(T)(size_t[] dims ...)
{
  string ret = "" ~ T.stringof;
  for(long i = (dims.length - 1); i >= 0; --i)
  {
    ret ~= text("[", dims[i], "]");
  }
  ret ~= text(" x", ";");
  ret ~= text("\nreturn x;");
  return ret;
}

In the above, we assign the array to variable x, this is fine because this is actually going to be a sub-function rather than something directly accessible by the programmer. Notice that we reversed the order of the indices internally because we want the first index specified to be the outer length of the array not the inner. The type if the array element is denoted by the template variable T, note also that the function is also an example of a variadic function.

We can also write a function to create a dynamic array in the form auto x = T[][]...[](n0, n1, ..., nk), given below:

string createDynamicArray(T)(size_t[] dims ...)
{
  string ret = "auto x = new " ~ T.stringof;
  string sqBrackets, rBrackets = "(";
  for(long i  = 0; i < (dims.length - 1); ++i)
  {
    sqBrackets ~= text("[", "]");
    rBrackets ~= text(dims[i], ", ");
  }
  sqBrackets ~= text("[", "]");
  rBrackets ~= text(dims[$-1], ")");
  ret ~= sqBrackets ~ rBrackets ~ ";";
  ret ~= text("\nreturn x;");
  return ret;
}

The function called by the programmer is:

auto createArray(T, bool dynamic = false)(size_t[] dims ...)
{
  string createStaticArray(T)(size_t[] dims ...)
  {
    string ret = "" ~ T.stringof;
    for(long i = (dims.length - 1); i >= 0; --i)
    {
      ret ~= text("[", dims[i], "]");
    }
    ret ~= text(" x", ";");
    ret ~= text("\nreturn x;");
    return ret;
  }
  string createDynamicArray(T)(size_t[] dims ...)
  {
    string ret = "auto x = new " ~ T.stringof;
    string sqBrackets, rBrackets = "(";
    for(long i  = 0; i < (dims.length - 1); ++i)
    {
      sqBrackets ~= text("[", "]");
      rBrackets ~= text(dims[i], ", ");
    }
    sqBrackets ~= text("[", "]");
    rBrackets ~= text(dims[$-1], ")");
    ret ~= sqBrackets ~ rBrackets ~ ";";
    ret ~= text("\nreturn x;");
    return ret;
  }
  static if(dynamic)
    enum arrayString = createDynamicArray!(double)(3, 4, 5);
  else
    enum arrayString = createStaticArray!(double)(3, 4, 5);
  mixin(arrayString);
}

Boolean dynamic variable allows us to state whether the array should be dynamic or not (static), and defaults to false. Example usage of the function:

writeln("Static array: ", createArray!(double)(2, 3, 4), "\n");
writeln("Dynamic array: ", createArray!(long, true)(2, 3, 4));

output:

Static array: [[[nan, nan, nan, nan, nan], [nan, nan, nan, nan, nan], 
  [nan, nan, nan, nan, nan], [nan, nan, nan, nan, nan]], [[nan, nan, nan, nan, nan],
  [nan, nan, nan, nan, nan], [nan, nan, nan, nan, nan], [nan, nan, nan, nan, nan]],
  [[nan, nan, nan, nan, nan], [nan, nan, nan, nan, nan], [nan, nan, nan, nan, nan],
  [nan, nan, nan, nan, nan]]]

Dynamic array: [[[nan, nan, nan, nan, nan], [nan, nan, nan, nan, nan], 
  [nan, nan, nan, nan, nan], [nan, nan, nan, nan, nan]], [[nan, nan, nan, nan, nan],
  [nan, nan, nan, nan, nan], [nan, nan, nan, nan, nan], [nan, nan, nan, nan, nan]],
  [[nan, nan, nan, nan, nan], [nan, nan, nan, nan, nan], [nan, nan, nan, nan, nan],
  [nan, nan, nan, nan, nan]]]

Summary

Multidimensional arrays in D are actually nested arrays. In this article we showed their basic functionality and how to work with them using string mixins.

Thank you!