A large program is usually made up of several routines each of which represents some characteristic part of the calculation. Such routines may be called in at several different points in the program, and their design and use is a fundamental feature of the language. The introductory example consisted of a main block only (delimited by begin and end of program) although it makes reference to the routines 'read', 'print', 'newline', which are permanently available in the machine. In exactly the same way however, the user may call in routines which he has written himself in Autocode language. Consider for example a routine to evaluate
y = a(m) + a(m+1)x+...........+ a(m+n)x↑n (n ≥ 0)
where the coefficients are selected from some vector a.
routine poly(real name y, array name a, real x, integer m,n) integer i y = a(m+n) ; return if n = 0 cycle i = m+n-1,-1,m y = x*y+a(i) repeat return end
Given the values of x,m,n and the addresses of y and the array elements a(i), it evaluates the polynomial and sets y to this value.
The statement end is the formal or written end of the routine while return is the dynamic end, i.e. it is the instruction which returns control to the main routine. Where the formal end is also a dynamic end as in the present example the return instruction preceding end can be omitted; in this case end serves for both purposes.
1: There can be any number of alternative exit points in a routine - i.e. return can occur more than once.
2: return is a member of the FORMAT CLASS[UI] - i.e. it can be made conditional, as above.
This routine can be embedded and used in a main routine as illustrated below.
begin real U, V, z, x ; integer m array b(0:15), c(0:50) routine spec poly (realname y, array name a, real x, integer m,n) --- --- poly(U,b,z,0,m) --- --- poly(V,c,x2,20,10) --- --- stop routine poly(realname y, array name a, real x, integer m,n) integer i y = a(m+n); return if n = 0 cycle i = m+n-1,-1,m y = x*y+a(i) repeat return end end of program
The routine is called in by the main routine whenever the name 'poly' appears. The first reference to 'poly' would cause the poly routine to evaluate
U = b(0) + b(1)z + ... + b(m)z↑m
and the second would cause it to evaluate
V = c(20) + c(21)x2 + ... + c(30)x↑20
The parameters in the routine specification and routine heading are the formal parameters and the parameters in the call statements are the actual parameters (see next section).
The body of the routine may be considered as a block delimited by routine and end, and the concepts of storage allocation, local and non-local names etc. apply to routines in exactly the sane manner as for blocks. In fact a block may be considered as being an open routine without parameters.
Any number of routines may be embedded in a main routine in the above fashion and they are referred to as subroutines of the main routine. If the body of a subroutine occurs before any reference to it in the main routine, the routine specification may be omitted, but by convention it is usual to place all the subroutine specifications among the declarations at the head of the main routine and the bodies at the end.
The parameters of the routine are the items of information which specify the action of the routine whenever it is used. The formal parameters are the names by which this information is referred to inside the routine itself, and the actual parameters are the names or expressions which are substituted for the formal parameters whenever the routine is used in the main program. For each type of formal parameter there is a permissible form for the actual parameter, as shown in the following table:-
Formal parameter type | Corresponding actual parameter |
---|---|
integer name | name of an integer variable |
real name | name of a real variable |
integer | an integer [EXPR] (similar to an integer assignment) |
real | a general(i.e. real or integer)[EXPR] (similar to a real assignment) |
integer array name | name of an integer array |
array name | name of a (real) array |
integer array | name of an integer array |
array | name of a (real) array (the difference between these and the previous pair of parameters is explained below) |
routine type i.e. routine real fn integer fn |
Sometimes it is required to pass on the name of a routine as a parameter. In this case the actual parameter is the name of a routine which must correspond in type and specification with the formal parameter, the specification of which will be found in the routine body |
In the example of a routine to evaluate a polynomial described earlier, the formal parameter y is the name of the variable to which the result is assigned, and the corresponding actual parameter must be a name, in this case the name of a real variable. The formal parameter then is of type real name. A reference to y inside the routine is essentially a reference to the non-local variable named by the actual parameter. The same applies to the array name parameter a, a reference to a inside the routine being a reference to the non-local array whose name is substituted for a in the calling statement.
The formal parameter real x on the other hand can be replaced by a general arithmetic expression, which is evaluated and assigned to the local variable x which is specially created in addition to any local real variables declared in the routine, The same applies to the formal parameters integer m,n. These are essentially local quantities, and expressions are substituted in place of them are evaluated and the resultant values assigned to the local integer variables m and n, which are lost on exit from the routine. Consequently the routine should place the information it produces in variables which are called by NAME (such as x and a).
The formal parameters x, m, n are said to be called by value in so far as it is only the values of the corresponding actual parameters which are of interest. This is the essential difference between the formal parameter types array and array name (or integer array and integer array name). In the former case the array named by the actual parameter is copied into a specially created local array, and a reference to the name in the routine is taken as referring to this local array. As the copying process can be time-consuming and space-consuming arrays should be called by name if at all possible, especially if they are large. Another example of a routine is the following
routine matmult(arrayname A,B,C integer p,q,r) integer i,j,k ; real c cycle i = 1,1,p cycle j = 1,1,r c = 0 cycle k = 1,1,q c = c+A(i,k)*B(k,j) repeat C(i,j) = c repeat repeat end
This forms the product of a p × q matrix A and a q × r matrix B. The result, a p × r matrix, is accumulated in C. The routine assumes that the first element of each matrix has the suffix (1,1). A typical call sequence might be
mat mult(N, x, y, 20, 20, 1)
where N, x, y had been declared by
array N(1:20,1:20), x,y(1:20,1:1)
When a routine has a single output value it may be written as a function routine and then used in an arithmetic expression in the same way as the permanent functions (cos, sin etc.). For example, the polynomial routine described earlier may be recast as a function routine as follows:-
real fn poly(arrayname a, real x, integer m,n) integer i ; real y y = a(m+n) ; if n = 0 then result = y cycle i = m+n-1,-1,m y = y*x+a(i) repeat result = y end
1: In general, the exit from a routine is of the form : result = [EXPR] and this causes the EXPRESSION on the right hand side to be evaluated as the value of the function.
2: result = [EXPR] acts as the dynamic end of a function (i.e. it corresponds to return in a routine), and may appear any number of times within the function.
3: result = [EXPR] is a number of the FORMAT CLASS[UI] - i.e. it may be made conditional.
The specification of the above routine would be written
real fn spec poly(arrayname a, real x, integer m,n)
and the routine can be called in an arithmetic statement, for example
y = a*b + 2h*poly(c,1/x,0,16)
An example of an integer function is given next. It selects the index of the maximum element x(k) in a set of array elements x(m), x(m+1),....,x(n) (n≥ m)
integer fn max(arrayname x, integer m,n) integer i,k k = m ->1 if n = m cycle i = m+1,1,n k = i if x(i) > x(k) repeat 1: result = k end
A call sequence for this function routine might be
y = 1 + x(max(x,1,100))
In general all names are declared at the head of a routine either in the routine heading or by the declarations integer, real, array, etc., and the various routine specifications.
Therefore they are local to that routine and independent of any names occurring in other routines. However, if a name appears in a routine which has not been declared in one of the above ways, then it is looked for outside i.e. in the routine or block in which it is embedded. If it is not declared there it is looked for in the routine or block outside that and so on until the main block is reached.
Now the main block is itself embedded in a permanent block at 'zero level' which contains the permanent material, so that if a name is not found in the main block it is looked for among these. The permanent names may in fact be redeclared locally at any level, but clearly it would be unwise to assign new meanings to such routines as 'log', 'print', etc. This outer block also contains supervisory material for controlling the entry to and exit from the main block. Very often, the only non-local names used in a routine will be the permanent names. The level at which a name is declared is sometimes referred to as its 'textual' level.
The permanent names include the standard functions, sin, log, int, etc. and the basic input/output routines read, print etc. These routines are used in a program without declaration and without the necessity of inserting the routine bodies, since these are permanently available at level zero. A full list of the permanent routines is given in Appendix 2.
[NOTE : the standard functions (and the same applies to 'read') are not strictly routines : THEIR NAMES CANNOT BE SUBSTITUTED AS ACTUAL PARAMETERS IN PLACE OF FORMAL PARAMETERS OF ROUTINE TYPE. they would first have to be redefined and renamed as formal routines.]
This is illustrated by the following example involving an integration routine
routine spec integrate(real name y, real a,b,integer n, real fn f)
which integrates a function f(x) over the range (a, b) by evaluating
y = (f(0) + 4f(1) + 2f(2) + ... + 4f(2n-1) + f(2n))(b-a)/(6n) where f(i) = f(a + ½i*(b-a)/n)
An auxiliary routine is required to evaluate f(x) and details of it must be passed on to the integration routine. This is done by means of the formal parameter type [RT] as defined earlier, and the body of the routine might then be:-
routine integrate (real name y, real a, b, integer n, real fn f) real fn spec f(real x) real h; integer i h = ½(b-a)/n y = 0 cycle i = 0,2,2n-2 y = y+2f(a+i*h)+4f(a+(i+1)h) repeat y = (y-f(a)+f(b))h/3 end
To enable instructions such as
y = y+2f(a+i*h)+4f(a+(i+1)h)
to be translated, a specification of the formal parameter f is required. In this case the delimiter real fn spec can be replaced by spec since the type of the function is given explicitly by the formal parameter itself. Now consider a programme to evaluate
z = exp(-y)cos(b*y)dy
for various values of b read from a data tape, the last value being followed by 1000, using for n the integer nearest to 10b.
begin routine spec integrate (real name y,real a,b,integer n,real fn f) real fn spec aux (real y) real z, b comment Simpson rule integration 1:read (b) if b = 1000 then stop integrate (z, 0, 1, int(10b), aux) newline print (b, 1, 2);spaces(2);print(z, 1, 4) -> 1 real fn aux(real y) result = exp(-y) cos(b*y) end ----------------- |routine integrate| ----------------- end of program
1: That the names given to the auxiliary routine and its parameters need not be the same in the integration routine as in the main program, but they must correspond in type.
2: Since the result of the integration is a single quantity, the routine could be recast as a real fn :-
real fn spec integrate (real a,b, integer n, real fn f)
and called by, for example:-
print(integrate(0,1,int(10b),aux),1,6)
The name of a routine is local to the routine or block in which its specification appears, and so the body of the routine is within the scope of its own name. Hence it may call itself. It may also call itself indirectly by invoking other routines which make use of it. On each activation of the routine a fresh copy of the local working space is set up in the stack, so that there will be no confusion between variables on successive calls. (This does not apply however to own variables. See next section.) Some criterion within the body of the routine must eventually inhibit the calling statement and allow the process to unwind. Functions defined recursively, for example:-
n! = n(n-1)! , n > 1 = 1 , n = 1
can be implemented in this way, but it is always more efficient to use recurrence rather than recursive techniques.
When a routine is left any information stored in variables corresponding to local declarations in that routine is lost, and no furthur reference may be made to it. In some cases it may be desirable to retain some of this information and be able to refer to it on a subsequent entry to the routine. This may be accomplished by prefixing the relevant declaration by own. For example
own real a, b; own array A (1:10)
The effect of own is to allocate storage space for the named variables in a part of the store which is not overwritten when other routines are called in and to set them to zero. This is done during the compiling of the program and hence does not have dynamic significance; as a consequence an own array declaration must have parameters which are integer constants.