Call a function: Difference between revisions

Line 852:
write(*,*) 'function in statement context: Does not apply!'
write(*,*) '-----------------'
write(*,*) 'Fortran passes memortymemory location of variables as arguments.'
write(*,*) 'So an argument can hold the return value.'
write(*,*) 'function result: ', g(5,8,lresult) , ' function successful? ', lresult
Line 914:
function in statement context: Does not apply!
-----------------
Fortran passes memortymemory location of variables as arguments.
So an argument can hold the return value.
function result: 13 function successful? T
Line 928:
-----------------
</pre>
 
As described in [[Naming_conventions#Fortran|Naming Conventions]], First Fortran (1958) allowed user-written functions but with restrictions on the names so that an ordinary variable called SIN would be disallowed because it was deemed to be in conflict with the library function SINF. These constraints were eased with Fortran II, and the rule became that a user could employ any correct-form name, such as SQRT, for a variable's name (simple or array) but then the library function SQRT would become inaccessible in such a routine. Similarly, there would be no point in the user writing a function called SQRT, because it could not be invoked - the compiler would take any invocation as being for the library routine SQRT. Thus, a user-written function could perhaps chance to have the name of an obscure (i.e. one forgotten about) library function, but if you were lucky it would have conflicting parameters and the compiler will complain.
 
A special case is provided by the "arithmetic statement function" that is defined after declarations but before executable statements in a routine and which has access to all the variables of the routine. Consider <lang Fortran> REAL this,that
DIST(X,Y,Z) = SQRT(X**2 + Y**2 + Z**2) + this/that !One arithmetic statement, possibly lengthy.
...
D = 3 + DIST(X1 - X2,YDIFF,SQRT(ZD2)) !Invoke local function DIST.</lang>
In this case, even if "DIST" happened to be the name of some library function, invocations within the routine defining it would not be of the library function.
 
Within a function there are some delicacies. The usual form is to assign the desired result to the name of the variable as in <code>H = A + B</code> where <code>H</code> is the name of the function. However, during evaluation the desired result might be developed over many stages and with reference to prior values. Suppose function H is to combine results from separate statements and it is not convenient to achieve this via one lengthy expression, perhaps because of conditional tests. Something like <lang Fortran> H = A + B
IF (blah) H = 3*H - 7</lang>
As written, the appearance of <code>H</code> on the right-hand side of an expression does ''not'' constitute an invocation of function <code>H</code> at all. Some compilers fail to deal with this as hoped, and so one must use a scratch variable such as </code>FH</code> to develop the value, then remember to ensure that the assignment <code>H = FH</code> is executed before exiting the function, by whatever route. If the result is a large datum (a long character variable, say) this is annoying.
 
With the belated recognition of recursive possibilities (introduced by Algol in the 1960s) comes the possibility of a function invoking itself. In the above example, <code>H(3.7,5.5,6.6)</code> would clearly be a function invocation (because of the parentheses) whereas <code>H</code> would not be. Actually, Fortran routines have always been able to engage in recursion, it is just the returns that will fail - except on a stack-based system such as the Burroughs 6700 in the 1970s.
 
Fortran also offers the ability to pass a function as a parameter, as in <lang Fortran> REAL FUNCTION INTG8(F,A,B,DX) !Integrate function F.
EXTERNAL F !Some function of one parameter.
REAL A,B !Bounds.
REAL DX !Step.
INTEGER N !A counter.
INTG8 = F(A) + F(B) !Get the ends exactly.
N = (B - A)/DX !Truncates. Ignore A + N*DX = B chances.
DO I = 1,N !Step along the interior.
INTG8 = INTG8 + F(A + I*DX) !Evaluate the function.
END DO !On to the next.
INTG8 = INTG8/(N + 2)*(B - A) !Average value times interval width.
END FUNCTION INTG8 !This is not a good calculation!
 
FUNCTION TRIAL(X) !Some user-written function.
REAL X
TRIAL = 1 + X !This will do.
END FUNCTION TRIAL !Not the name of a library function.
 
PROGRAM POKE
INTRINSIC SIN !Thus, not an (undeclared) ordinary variable.
EXTERNAL TRIAL !Likewise, but also, not an intrinsic function.
REAL INTG8 !Don't look for the result in an integer place.
WRITE (6,*) "Result=",INTG8(SIN, 0.0,8*ATAN(1.0),0.01)
WRITE (6,*) "Linear=",INTG8(TRIAL,0.0,1.0, 0.01)
END</lang>
This involves a fair amount of juggling special declarations so that the compiler will make the desired assumptions that a function is being called upon, rather than the value of some variable. This is eased somewhat with F90 onwards if the MODULE protocol is used so that at least you do not have to remember to declare INTG8 as REAL. Certain library functions are not allowed as candidates for passing to INTG8 (for instance, the compiler may render them as in-line code, bypassing the protocols used for functions) and arithmetic statement functions are usually rejected also. Arithmetic expressions are not allowable as possible "functions" either - how might f(x) = sin(x) + 3*sqrt(x) + 7 be expressed? As <code>INTG8(SIN + 3*SQRT + 7,''etc...''</code>? A properly-named function must be declared. And of course, candidate functions must have the correct number and type of parameters, or else...
 
This works because Fortran passes parameters by reference (i.e. by giving the machine address of the entity), so that for functions, the code's entry point for the function is passed. With normal variables this means that a function (or subroutine) might modify the value of a parameter, as well as returning the function's result - and also mess with any COMMON data or other available storage, so a function EATACARD(IN) might read a line of data into a shared work area (called say ACARD) from I/O unit number IN and return ''true'', otherwise ''false'' should it hit end-of-file.
 
But it is also possible that parameters are passed via copy-in copy-out instead of by reference, with subtle changes in behaviour. This may also be done even on systems that do employ passing by reference. For instance, with <lang Fortran> TYPE MIXED
CHARACTER*12 NAME
INTEGER STUFF
END TYPE MIXED
TYPE(MIXED) LOTS(12000)</lang>
One might hope to try <code>IT = BCHOP(LOTS.NAME,"Fred")</code> where BCHOP is a well-tested function for performing a binary search that should run swiftly. Alas, no. The successive values of NAME are not contiguous while BCHOP expects to receive an array of values that are contiguous. So, the compiler inserts code to copy all the LOTS.NAME elements into such a work area and passes the location of that to BCHOP (which searches it swiftly), then on return, the work area is copied back to LOTS.NAME just in case. This latter can be avoided if within BCHOP its array is given the attribute INTENT(IN) but the incoming copy still means an effort of order N, while the search accesses Log(N) only. This can have a less-than-subtle effect if large arrays are involved.
 
=={{header|Go}}==
1,220

edits