Subsections

Assigning to multiple names

We can assign values to the elements of a multiple either individually or collectively.

Individual assignment

You may remember from chapter 3 that we can access an individual element of a multiple by specifying the subscript(s) of that element. For example, suppose that we wish to access the third element of i7 as declared in the last section. The rules of the language state that a subscripted element of a multiple name is itself a name. In fact, the elaboration of a slice of a multiple name creates a new name. Thus the mode of i7[3] is REF INT. We can assign a value to i7[3] by placing the element on the left-hand side of an assignment:

   i7[3]:=4

Unless you define a new identifier for the new name, it will cease to exist after the above assignment has been elaborated (see below for examples of this).

Since each element of i7 has an associated name (created by slicing) of mode REF INT, it can be used in a formula:

   i7[2]:=3*i7[i7[1]] + ENTIER(4.0/i7[3])

As you can see, an element was used to compute a subscript. It has been presumed that the value obtained after dereferencing lies between 1 and 7 inclusive. If this were not so, a run-time error would be generated. In the above assignment, all three elements on the right-hand side of the assignment would be dereferenced before being used in the formula. Note that subscripting (or slicing or trimming) binds more tightly than any operator. Thus, in the last term in the above example, i7 would be sliced first, then the yielded name dereferenced, and finally, the new value would be divided into 4.0.

Here is a FOR loop which assigns a value to each element of i7 individually:

   FOR e FROM LWB i7 TO UPB i7
   DO
      i7[e]:=e**3
   OD

Using the bounds interrogation operators is useful because:

  1. The fact that the lower bound of i7 is 1 is masked, but the formula LWB i7 ensures that the correct value is used.
  2. If the bounds of i7 are changed when the program is being maintained, the loop clause can remain unchanged. This simplifies the maintenance of Algol 68 programs.
  3. The compiler can omit bounds checking. For large multiples, this can speed up processing considerably.

Here is a program which uses a name whose mode is
REF[]BOOL. It computes all the prime numbers less than 1000 and is known as Eratosthenes' Sieve:

   PROGRAM sieve CONTEXT VOID
   USE standard
   BEGIN
      INT size = 1000;

      REF[]BOOL flags = LOC[2:size]BOOL;

      FOR i FROM LWB flags TO UPB flags
      DO
         flags[i] := TRUE
      OD;

      FOR i FROM LWB flags TO UPB flags
      DO
         IF flags[i]
         THEN
            FOR k
            FROM 2*i BY --i TO UPB flags
            DO
               flags[k] := FALSE
               CO Remove multiples of i CO
            OD
         FI
      OD;

      FOR i FROM LWB flags TO UPB flags
      DO
         IF flags[i] THEN print((i,blank)) FI
      OD
   END
   FINISH

Collective assignment

There are two ways of assigning values collectively. Firstly, it can be done with a row-display or a []CHAR denotation. For example, using the declaration of i7 above:

   i7:=(4, -8, 11, ABS "K",
        ABS TRUE, 0, ROUND 3.4)

Notice that the bounds of both i7 and the row-display are [1:7]. In the assignment of a multiple, the bounds of the multiple on the right-hand side must match the bounds of the multiple name on the left-hand side. If they differ, a fault is generated. If the bounds are known at compile-time, the compiler will generate an error message. If the bounds are only known at run-time (see section 5.8 on dynamic names), a run-time error will be generated. The bounds can be changed using a trimmer or the @ symbol (or AT). See chapter 3 for details.

The second way of assigning to the elements of a multiple collectively is to use an identifier of a multiple with the required bounds. For example:

   []INT i3 = (1,2,3);
   REF[]INT k = LOC[1:3]INT := i3

The right-hand side has been assigned to the multiple name k.

As mentioned above, parts of a multiple can be assigned using slicing or trimming. For example, given the declarations

   REF[,]REAL x = LOC[1:3,1:3]REAL,
              y = LOC[0:2,0:2]REAL

and the assignment

   x:=((1,2,3),
       (4,5,6),
       (7,8,9))

we can write

   y[2,0]:=x[3,2]

The multiple name y is sliced yielding a name of mode REF INT. Then6.5 the multiple name x is sliced also yielding a name of mode REF INT which is then dereferenced yielding a new instance of the value to which it refers (8) which is then assigned to the new name on the LHS of the assignment. Here is an identity-declaration which makes the new name permanent:

   REF INT y20 = y[2,0];  y20:=x[3,2]

which has its uses (see below).

Here are some examples of slicing with (implied) multiple assignments:

   y     := x[@0,@0];
   y[2,] := x[ 1,@0];
   y[,1] := x[ 2,@0]

In the first example, the right-hand side is a slice of a name whose mode is REF[,]REAL. Because the slice has no trimmers its mode is also REF[,]REAL. Using the @ symbol, the lower bounds of both dimensions are changed to 0, ensuring that the bounds of the multiple name thus created match the bounds of the multiple name y on the left. After the assignment (and the dereferencing), y will refer to a copy of the multiple x and the name created by the slicing will no longer exist.

In the second assignment, the multiple x has been sliced yielding a name whose mode is REF[]REAL. It refers, in fact, to the first “row” of x. The @0 ensures that the lower bound of the second dimension of x is 0. The left-hand side yields a name of mode REF[]REAL which refers to the last “row” of the multiple y. The name on the right-hand side is dereferenced. After the assignment y[2,] will refer to a copy of the first “row” of x and the name produced by the slicing will no longer exist.

In the third assignment, the second “row” of x is assigned to the second “column” of y. Again, the @0 construction ensures that the lower bound of the second dimension of x is zero. After the assignment, the name created by the slicing will no longer exist.

Notice how the two declarations for x and y have a common formal-declarer on the left-hand side, with a comma between the two declarations. This is a common abbreviation. The comma means that the two declarations are elaborated collaterally (and on a parallel processing computer, possibly in parallel).

It was stated in the section on names that names can be put on the right-hand side of an identity declaration. This is particularly useful for accessing elements of rows. Consider the following:

   REF[]INT r = LOC[100]INT;

   FOR i FROM LWB r TO UPB r DO r[i]:=i*i OD;

   FOR i FROM LWB r TO UPB r-1
   DO
      IF   REF INT ri=r[i], ri1=r[i+1];
           ri > ri1
      THEN ri:=ri1
      ELSE ri1:=ri
      FI
   OD

This is another example of optimisation, but in this case, we need names because the THEN and ELSE clauses contain assignments. Both ri and ri1 are used thrice in the conditional clause, but the multiple r is only subscripted twice in each loop. In the condition following the IF, both ri and ri1 would be dereferenced (but not in the identity declarations). The values of ri and ri1 remain constant: the names are assigned new values. You can see from the identity declarations that the modes of the names ri and ri1 are both REF INT.

Here is a program fragment which uses a REF[]REAL identity declaration for optimisation:

   REF[,]REAL m = LOC[3,4]REAL;   read(m);
   
   FOR i FROM 1 LWB m TO 1 UPB m
   DO
      REF[]REAL mi = m[i,];
      FOR j FROM LWB mi TO UPB mi
      DO
         REF REAL mij = mi[j];
         mij*:=mij
      OD
   OD;
   
   print((m,newline))

As you can see, read behaves just like print in that a whole multiple can be read at one go (see chapter 3 for the use of print with multiples). The only difference between the way read is used and the way print is used is that the values for read must be names (or identifiers of names) whereas print can use denotations or identifiers of names or identifiers which are not names.


Exercises

5.7
After the assignments of x to y discussed above, what is the final value of y (careful)? Ans[*]
5.8
Given these declarations
   REF[,]INT m = LOC[3:5,-2:0]INT,
   REF[]INT n = LOC[1:3]INT:=(1,2,3)
Ans[*]
(a)
What is wrong with the assignment m[1,]:=n?

(b)
How would you assign the second “column” of m to its third “row”?

5.9
Modify Eratosthenes' Sieve to compute the 365th prime. Ans[*]


Sian Mountbatten 2012-01-19