Rather than using pass-by-reference for input/output parameters, Algol used the more powerful mechanism of pass-by-name.
In essence, you can pass in the symbolic "name"; of a variable, which allows it both to be accessed and updated.
For example, to double the value of C[j]
, you can
pass its name (not its value) into the following procedure.
procedure double(x); real x; begin x := x * 2 end;
In general, the effect of pass-by-name is to
textually
substitute the argument expressions in a procedure call
for the corresponding parameters in the body of the
procedure, e.g., double(C[j])
is interpreted as
C[j] := C[j] * 2
.
Technically, if any of the variables in the called procedure clash with the caller's variables, they must be renamed uniquely before substitution.
Implications of the pass-by-name mechanism:
Passing expressions into a routine so they can be repeatedly evaluated has some valuable applications.
Consider calculations of the form: "sum xi
× i
for all i from 1 to n."
How could a routine Sum
be
written so that we could express this as
Sum(i, 1, n, x[i]*i)
?
Using pass-by-reference or pass-by-value, we cannot
do this because we would be passing in only a single
value of x[i]*i
, not an expression which
can be repeatedly evaluated as "i
" changes.
Using pass-by-name,
the expression x[i]*i
is passed in without evaluation.
real procedure Sum(j, lo, hi, Ej); value lo, hi; integer j, lo, hi; real Ej; begin real S; S := 0; for j := lo step 1 until hi do S := S + Ej; Sum := S end;Each time through the loop, evaluation of
Ej
is actually the evaluation of the expression x[i]*i
= x[j]*j
.
procedure swap (a, b); integer a, b, temp; begin temp := a; a := b; b:= temp end;
swap(x, y)
:
temp := x; x := y; y := temp
swap(i, x[i])
:
temp := i; i := x[i]; x[i] := tempIt doesn't work! For example:
Before call: | i = 2 | x[2] = 5 | |
After call: | i = 5 | x[2] = 5 | x[5] = 2
|
It is very difficult to write a correct swap
procedure in Algol.
boolean procedure and (x, y); boolean x, y; begin if x then return y else return false end;Here,
y
is not evaluated if x
is false.
Disadvantages:
Thunks are parameterless procedures used to implement pass-by-name.
Essentially a thunk is set up to return a pointer to the current location represented by a parameter.
Thunks can be simulated in (extended) Pascal using procedure types.
For example, to simulate the argument expressions i
and x[i]
passed using pass-by-name, the following thunks can be set up.
TYPE IntVarAddress = ^ INTEGER; RealVarAddress = ^ REAL; FUNCTION iThunk () : IntVarAddress; BEGIN iThunk := ADDRESS(i) END;FUNCTION XiThunk () : RealVarAddress; VAR expi : REAL; BEGIN expi := x[i] * i; XiThunk := ADDRESS(expi) END;
Notice that iThunk
will always return the same memory location,
but that XiThunk
will return a memory location dependent on
the current value of i
.
Now, the Algol expression Sum(i, 1, n, x[i])
could be rewritten in (extended)
Pascal as Sum(iThunk, lo, hi, XiThunk)
where the Sum
function is defined as follows.
FUNCTION Sum(jThunk : FUNCTION () : IntVarAddress; lo, hi : INTEGER; EjThunk : FUNCTION () : RealVarAddress); VAR S : REAL; BEGIN S := 0; jThunk()^ := lo; WHILE jThunk()^ <= hi DO BEGIN S := S + EjThunk()^; jThunk()^ := jThunk()^ + 1 END; Sum := S END;