Programming Corner from Icon Newsletter 13


May 29, 1987; Icon Version 5

Random Numbers

In the last Newsletter, the question of independent random sequences was raised. One of the problems posed there was to write a procedure random() whose result sequence consists of the successive values of &random as it changes as a side effect of the evaluation of ?x, assuming there are no other uses of random generation in the program in which random() is used. This is easily done, and such a procedure is
procedure random()
   repeat {
      suspend &random
      ?0
      }
end
The only point here is the observation that each evaluation of ?0 (or any other valid form of ?expr) changes &random.

Now the problem is how to achieve this same sequence of results if there are other occurrences of ?expr in the program. Some method of remembering the value of &random is needed. This is not hard to do in a procedure, but it requires a little care. A solution is
procedure random()
   local i

   suspend i := 0
   repeat {
      &random :=: i
      ?0
      i :=: &random
      suspend i
      }
end
Here, it is necessary to know that the initial value of &random is 0, since there may be random generation before random() is called. The local identifier i keeps track of the value of &random, which may be changed between suspensions and resumptions of random(). The values of &random and i are exchanged so that random() does not affect other uses of ?expr -- independence works two ways.

So far, so good. But what about an expression that generates the successive values of &random without using a procedure? The procedure provides a loop from which values are produced, but there is no corresponding expression form in Icon. For example, there is no way to deliver a value out of a while or every loop. To do this with an expression requires rephrasing the procedural solution as a mutual evaluation expression, in which backtracking is used to assure that &random has the correct value on each resumption. Such an expression is
(i := 0) | (i :=: &random,|?0,i <-> &random)
The first expression in the alternation sets i to the initial value of &random which is known to be 0, and also produces this as the first value of the whole expression. The second expression in the alternation is a mutual evaluation of three expressions. The first of these expressions exchanges the values of i and &random, so that i now contains whatever value &random had outside the expression and &random is properly initialized. The second expression changes the value of &random (the reason for repeated alternation will become apparent in a moment). Next, the values of i and &random are exchanged again. Since the exchange operation returns its left argument, the value of the entire mutual evaluation expression is the desired result.

The interesting aspect of this expression occurs when it is resumed to produce another value. The last expression, the reversible exchange, is resumed. This causes the values of i and &random to be restored to what they were before the reversible exchange was evaluated. Specifically, &random is restored to the value it had after the preceding evaluation of |?0, regardless of what happened while the mutual evaluation expression was suspended. Since the reversible exchange does not produce a second result when it is resumed, but only reversed the exchange, |?0 is resumed next. Here the reason for repeated alternation is evident -- it always produces another result and as a side effect advances the random sequence. With this new result, the reversible exchange is evaluated again to produce the next value of &random, and so on. Note that the first expression in the mutual evaluation is never resumed; it serves only as initialization and the repeated alternation provides a barrier to backtracking, since it always produces another result.

Admittedly, this mutual evaluation expression is somewhat arcane. However, once the concepts are grasped, such techniques become idiomatic.

Getting away from the problem of independently generating the values of &random, which was chosen only to make the problem simple, there are cases in which other independent random sequences are useful. One is a random "production/consumption" kind of process. This is illustrated in the following program, which creates randomly positioned stars on a terminal screen, building up an initial field of stars, after which the oldest stars are destroyed in the order in which they were created. Finally, creation ceases and destruction continues until all the stars are gone. To accomplish this, two identical sequences of co-ordinate positions are used, one for creation, and one for destruction. Creation is started up first and allowed to proceed until the destruction process is started. There is no need to provide storage for the co-ordinate positions, since this is done in the expressions as described above. Co-expressions are used so that the creation and destruction of stars can be controlled. The program is:
procedure main(x)
   local i, j, r, ran1, ran2

   i := x[1] | 10            # time for creation/destruction
   j := x[2] | 50            # steady state time
   r := 0
   ran1 := create (r := 0,&random :=: r,rplot("*"),
      &random <-> r)
   ran2 := create (r := 0,&random :=: r,rplot(" "),
      &random <-> r) clear() # clear the screen
   every 1 to i do @ran1     # create the universe
   every 1 to j do {         # steady state condition
      @ran2
      @ran1
      }
   every 1 to i do @ran2     # destroy the universe
   home()                    # home the cursor
end
Note that the times for startup/destruction and the steady-state times can be provided optionally as command line arguments. The identifiers r in the co-expressions for ran1 and ran2 are distinct, since the creation of a co-expression creates independent copies of local identifiers.

The procedures rplot(s), clear(), and home() are terminal-dependent. The last two clear the screen and home the cursor, respectively. The interesting routine is rplot(s), which is a generator that on successive resumptions writes s at randomly selected spaces on the screen. For the DataMedia 3025, rplot is:
procedure rplot(s)
   static row, col
   initial {
      row := string(&cset[33+:24])
      col := string(&cset[33+:80])
      }
   suspend |writes("\^[Y",col[?80],row[?24],s)
end
Question: Can the repeated alternation in
suspend |writes("\^[Y",col[?80],row[?24],s)
be placed at any other position other than in front of writes?


Icon home page