12.5 Destructive Functions

Export of DataStream

set!: (%, MachineInteger, T) -> T

Usage

T == Integer;
l: List T := [4,3,5,2,6,1];
s: DataStream T := stream generator l;
s.3 := 1;
u: List T := [t-z for t in l for z in s];

a: DataStream := stream 0;
a.2 := 3;
v: List T := [z for t in 0..4 for z in a]
assert(v=[0,0,3,0,0];

Description

Destructively modify an entry of a stream.

The function uses the existing stream data and modifies it in the given place.

If the stream s is known to be constant, then it is also constant after the call set!(s, n, t).

Remarks

This function changes the value of the stream at exactly one place.

416exports: DataStream 392a+   (386)  412  419
set!: (%, MachineInteger, T) -> T;

Uses MachineInteger 67.

Let us try to set the i-th element of a stream x to t. The simplest approach is to access the i-th element in order to compute all elements up to xi and then override the i-th entry. According to the way we have stored elements in the stream, the following situation could happen. The stream will eventually be constant, but we have not yet stepped the generator enough to know for sure. In fact, i could just be the position of the constant element that should be repeated infinitely often and the internal generator is empty. If in that case we simply override xi by a new element t the stream would be modified in infinitely many places, since it would give xn = t for all n i. We simply exclude that case by computing up to the index i + 1.

417asetElement: compute enough entries 417a  (418c)
u: T := x(next i);

Still, there are two cases.

  1. The size of the underlying array is at least i + 1.
  2. The stream is constant and the size of the cache is less than i + 1.

In the first case, we can simply override the element in the cache.

417bsetElement: treat constant and non-constant stream 417b  (418c)
numOfElements := #x;
if i < prev numOfElements then {
        X.cache.i := t;
} else {
        setElement: constant stream 417c
}

In the second case the stream must be known to be constant.

417csetElement: constant stream 417c  (417b)  418a
assert(constant? x);

Then we make room in the cache to hold the new element t and the old constant u as the next element so that u is also the (repeated) constant of the new stream.

418asetElement: constant stream 417c+   (417b)  417c  418b
resize!(x, i+2);

And we fill the uninitialized entries with the constant u from above.

418bsetElement: constant stream 417c+   (417b)  418a
a: PrimitiveArray T := X.cache;
for k in numOfElements .. prev i repeat a.k := u;
a.i := t;
a(next i) := u;
X.numberOfElements := i+2;

Putting it all together yields the following chunk.

418cimplementation: DataStream 392b+   (386)  413b  420
set!(x: %, i: I, t: T): T == {
        setElement: compute enough entries 417a
        setElement: treat constant and non-constant stream 417b
        t;
}

Uses I 47.

Export of DataStream

set!: (%, Generator T) -> ()

Usage

T == Integer;
l: List T := [4,3,5,2,6,1];
s: DataStream T := stream generator l;
a := s.3;
g: Generator l;
set!(s, g);
b := s.5;

fib: DataStream T := new();
g: Generator T := generate {
  yield 1;
  yield 1;
  for n in 0.. repeat yield fib.n + fib.(next n);
}
set!(fib, g);
f: List T := [x for x in fib while x < 50];

Description

Construct a stream using an old structure.

The function uses the existing stream data of its first argument and creates a stream that does not modify the values that are already cached, but uses the new generator g for the generation of the next elements.

If the stream s is known to be constant, then after the call set!(s, g), the function call constant?(s) returns false.

Note that the stream is 0-based. In the usage example above, a = 2 (the fourth element of the list), and b = 3. The value of f is [1,1,2,3,5,8,13,21,34].

419exports: DataStream 392a+   (386)  416  422a
set!: (%, Generator T) -> ();

Uses Generator 617.
420implementation: DataStream 392b+   (386)  418c  422b
set!(x: %, g: Generator T): () == {
        X.gen := g;
        X.constant? := false;
}

Uses Generator 617.