ZetCode

Algol 68 rows

last modified November 13, 2026

A row is a sequence of values of the same mode, indexed by integers. Algol 68 calls arrays rows because the concept is orthogonal: a row of integers, a row of rows of reals, and a row of characters are all built by the same rules. This article describes how a row is declared, how its bounds are read, how its elements are reached, and how rows are passed to procedures.

Declaring a row

A row denotation writes the elements in parentheses separated by commas, and an identity declaration with empty brackets infers the bounds from the number of elements. The bounds then run from 1 to the count.

declare.a68
BEGIN
  []INT nums = (10, 20, 30, 40);
  print((whole(LWB nums, 0), " ", whole(UPB nums, 0), newline));
  print((whole(nums[1], 0), " ", whole(nums[4], 0), newline))
END

The identity declaration uses =, which means that the bounds are fixed by the denotation and never change. Such a row is also constant: its elements cannot be assigned, and a row that is meant to change is declared with := and explicit bounds. LWB and UPB return the lower and upper bound, and a subscript in brackets selects one element. Reading nums[5] would be an error, because the row has four elements.

$ a68g declare.a68
1 4
10 40

Explicit bounds

A row declared with := needs explicit bounds, written as [lower:upper] before the mode name. The bounds may differ from 1 and the count, but a row denotation always has bounds 1 to its length, so the denotation cannot initialise a row whose bounds start elsewhere.

bounds.a68
BEGIN
  [1:5] INT row := (1, 2, 3, 4, 5);
  [3:7] INT shifted;
  FOR i FROM LWB shifted TO UPB shifted DO
    shifted[i] := i * 10
  OD;
  print((whole(LWB row, 0), " ", whole(UPB row, 0), newline));
  print((whole(shifted[3], 0), " ", whole(shifted[7], 0), newline))
END

The first row is initialised from a denotation because its bounds are 1 to 5. The second one starts at 3, so it is filled in a loop instead, and its elements run from 30 to 70. Writing [3:7] INT shifted := (1, 2, 3, 4, 5) is rejected with a message about different bounds.

$ a68g bounds.a68
1 5
30 70

Walking through a row

A loop that runs from LWB to UPB visits every element, whatever the bounds are. This pattern works for rows and for strings alike, because both report their bounds in the same way.

walk.a68
BEGIN
  []INT temps = (12, 19, 7, 23, 15);
  INT total := 0, largest := temps[LWB temps];
  FOR i FROM LWB temps TO UPB temps DO
    total +:= temps[i];
    IF temps[i] > largest THEN largest := temps[i] FI
  OD;
  print((whole(total, 0), " ", whole(largest, 0), newline))
END

The total of the five values is 76, and the largest is 23. The variable largest starts with the first element rather than with zero, so the procedure also works for a row of negative numbers. An empty row would have upper bound 0, and the loop would run zero times.

$ a68g walk.a68
76 23

Slices

A slice written as row[from:to] produces a row of its own. When it initialises a declared row, the elements are copied; when it stands on the left of an assignment, the assignments reach the original row.

slice.a68
BEGIN
  [1:5] INT row := (1, 2, 3, 4, 5);
  [1:3] INT copy := row[2:4];
  copy[1] := 99;
  print((whole(row[2], 0), " ", whole(copy[1], 0), newline));
  row[2:4] := (7, 8, 9);
  print((whole(row[2], 0), newline))
END

The copy holds the values 2, 3, and 4 with bounds 1 to 3. Assigning 99 to its first element does not touch row[2], which is still 2. The second assignment writes through the slice instead, so row[2] becomes 7. This works because row was declared with :=: a row initialised with = from a denotation is a constant, and its elements cannot be assigned.

$ a68g slice.a68
2 99
7

Two-dimensional rows

Additional dimensions are written with commas inside the brackets, so [,]INT is a row of rows of integers. The upper bound of a single dimension is read by writing that dimension before UPB, as in 1 UPB grid.

two.a68
BEGIN
  [,]INT grid = ((1, 2, 3), (4, 5, 6));
  print((whole(1 UPB grid, 0), " ", whole(2 UPB grid, 0), newline));
  FOR i TO 1 UPB grid DO
    FOR j TO 2 UPB grid DO
      print((whole(grid[i, j], 0), " "))
    OD;
    print((newline))
  OD
END

The denotation has two rows of three elements, so the first dimension has upper bound 2 and the second has 3. A subscript pair such as grid[2, 3] selects one element, and the nested loops print the grid row by row. UPB grid without a dimension returns the bound of the first dimension.

$ a68g two.a68
2 3
1 2 3
4 5 6

Rows as parameters

A procedure parameter of mode []INT accepts a row of integers with any bounds, and the procedure reads those bounds from its parameter. This makes the same procedure usable for rows that start at 1 and for rows that start somewhere else.

procarg.a68
PROC total = ([]INT row) INT:
  ( INT sum := 0;
    FOR i FROM LWB row TO UPB row DO sum +:= row[i] OD;
    sum );

BEGIN
  []INT a = (1, 2, 3);
  [10:12] INT b;
  FOR i FROM LWB b TO UPB b DO b[i] := i - 6 OD;
  print((whole(total(a), 0), " ", whole(total(b), 0), newline))
END

The first call passes a row with bounds 1 to 3 and gets 6. The second row has bounds 10 to 12 and holds the values 4, 5, and 6, so the same loop sums it to 15. Had the loop counted from 1 to 3, it would have read two elements outside the row and missed one inside it.

$ a68g procarg.a68
6 15

Rows of strings

The element mode of a row may be any mode, including STRING. A row of strings is declared like any other row, and a subscript yields one string.

names.a68
BEGIN
  []STRING names = ("Alice", "Bob", "Carol");
  FOR i TO UPB names DO
    print((whole(i, 0), ": ", names[i], newline))
  OD
END

Each element is a separate string, and the loop prints an index next to each name. The strings in this row are created by the denotation, so each one has the bounds of its own denotation: Alice runs from 1 to 5, Bob from 1 to 3, and Carol from 1 to 5.

$ a68g names.a68
1: Alice
2: Bob
3: Carol

Next steps

The strings article applies the same operations to rows of characters, and the flexible rows article explains rows whose bounds may change. The loops article describes the clauses used to walk a row, and procedures shows how the row parameters above are declared.

Source

Revised Report on the Algorithmic Language Algol 68

The Algol 68 Genie project

In this article we have declared rows with inferred and explicit bounds, read their bounds, walked through their elements, copied a slice, used a two-dimensional row, passed rows to procedures, and stored strings in a row. The flexible rows article continues with rows that change size.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Algol 68 tutorials.