Array nested
An array's element type can be another array, and that is all a two-dimensional array is. A grid of three rows of four numbers is an array of three rows, where each row is an int32[4]. Boards, tables, images and spreadsheets all start out this way.
Built inside out, read outside in
The surprise is the order in the type. Each [n] wraps everything written before it, so the type is built inside out:
flowchart LR
e["int32<br/>one number"] --> r["int32[4]<br/>one row of 4 numbers"]
r --> g["int32[4][3]<br/>3 of those rows"]Indexing reads the other way, outside in: grid[row] picks a row, and grid[row][column] picks a number within it.
let grid: int32[4][3] = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
];
| column 0 | column 1 | column 2 | column 3 | |
|---|---|---|---|---|
| row 0 | 1 | 2 | 3 | 4 |
| row 1 | 5 | 6 | 7 | 8 |
| row 2 | 9 | 10 | 11 | 12 |
So grid[2][1] is row 2, column 1: 10. The outer length counts rows and a row's length counts columns:
PrintLine("{} rows of {} columns", grid.length, grid[0].length);
Walking by rows
for over the grid yields whole rows, and an inner for walks each row:
for row in grid {
var sum: int32 = 0;
for value in row {
sum += value;
}
PrintLine("row {} {} {} {} adds up to {}", row[0], row[1], row[2], row[3], sum);
}
row is an ordinary int32[4], so it has a length, it can be indexed, and it can be walked like any other array.
Walking by columns
There is no "column" value to loop over — columns cut across the rows. Going down a column means holding the column fixed and stepping through the rows by index:
for column in 0..grid[0].length {
var total: int32 = 0;
for row in 0..grid.length {
total += grid[row][column];
}
PrintLine("column {} adds up to {}", column, total);
}
Repeating a row
A repeated array nests too. [0; 3] is a row of three zeros, and [[0; 3]; 3] is three copies of that row — a blank board to fill in:
var board: int32[3][3] = [[0; 3]; 3];
for i in 0..board.length {
board[i][i] = 1;
}
Setting board[i][i] for each i puts a 1 on the diagonal. Because each row is a separate copy, changing one row never touches another.
The program
The whole lesson is one package in the Examples repository. Its comments explain every step.
// An array's element type can be another array, and that is all a two-dimensional array is. A
// grid of three rows of four numbers is an array of three rows, where each row is an `int32[4]`.
//
// The surprise is the order in the type. Each `[n]` wraps everything written before it, so the
// type is built inside out: `int32[4]` is one row, and `int32[4][3]` is three of those rows.
// Indexing reads the other way, outside in: `grid[row]` picks a row, and `grid[row][column]`
// picks a number within it.
import Io::PrintLine;
func Main() -> int {
// Three rows, each a literal of four numbers.
let grid: int32[4][3] = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
];
// The outer length counts rows; a row's length counts columns.
PrintLine("{} rows of {} columns", grid.length, grid[0].length);
PrintLine("grid[2][1] is {}", grid[2][1]);
// `for` over the grid yields whole rows, and an inner `for` walks each row.
for row in grid {
var sum: int32 = 0;
for value in row {
sum += value;
}
PrintLine("row {} {} {} {} adds up to {}", row[0], row[1], row[2], row[3], sum);
}
// Going down a column means holding the column fixed and stepping through the rows.
for column in 0..grid[0].length {
var total: int32 = 0;
for row in 0..grid.length {
total += grid[row][column];
}
PrintLine("column {} adds up to {}", column, total);
}
// A repeated array nests too: three copies of a row of three zeros, filled in afterwards.
var board: int32[3][3] = [[0; 3]; 3];
for i in 0..board.length {
board[i][i] = 1;
}
for row in board {
PrintLine("{} {} {}", row[0], row[1], row[2]);
}
return 0;
}
Run it
cd Examples/Sequences/ArrayNested
rux run
3 rows of 4 columns
grid[2][1] is 10
row 1 2 3 4 adds up to 10
row 5 6 7 8 adds up to 26
row 9 10 11 12 adds up to 42
column 0 adds up to 15
column 1 adds up to 18
column 2 adds up to 21
column 3 adds up to 24
1 0 0
0 1 0
0 0 1
Common mistakes
Three rows of four is
int32[4][3], not int32[3][4]. The wrong order fails with error: cannot assign 'int[4][3]' to 'int32[3][4]' — the message shows the shape the literal really has.grid[1, 2] is a syntax error: expected ']' to close the index expression before ','. Each level takes its own brackets: grid[1][2].Every row has the same type, so a short row is refused:
error: array element 2 has type 'int[3]', but the array's element type is 'int32[4]'.Try it yourself
- Add up every number in
gridwith two nestedforloops. - Print
gridwith its rows and columns swapped, so that the first line reads1 5 9. - Build a multiplication table,
var table: int32[10][10] = [[0; 10]; 10];, and fill it with(row + 1) * (column + 1). The indices areuint, so the product needsas int32. - Change the type of
gridtoint32[3][4]and read the error.
Learn more
- Arrays and Indexing and iteration in the Rux Reference
- Array repeat — the
[value; count]form used forboard - Slice — passing arrays of any length to one function