• No results found

Introducing external procedures

In document Fortran 95 (Page 123-137)

We have already come across several of Fortran’s intrinsic procedures, both functions and subroutines. This chapter explains how the programmer can write and use additional

“external” functions and subroutines. External procedures are not only useful for carrying out calculations not covered by the intrinsic procedures; they are necessary also for the clear organization and subdivision of any large Fortran program.

The statements covered here are FUNCTION, SUBROUTINE, END FUNCTION, END SUBROUTINE, and CALL. The important keyword INTENT is also introduced. This chapter includes extended sets of exercises.

7.1 Functions

A function is used to calculate a result whose value will generally depend on the values of “arguments”

given each time that the function is invoked. A function is like a mini-program and has an overall structure deliberately similar to a main program: it starts with a title statement, followed by non-executable declaration statements, and then the series of executable statements finishing finally with a version of the END statement. An example is

FUNCTION Cot(x) REAL :: Cot

REAL, INTENT(IN) :: x REAL :: s, c

IF (x==0.0) THEN

WRITE (*,*) "Error: cotangent function called with &

&zero argument"

Cot = HUGE(x)

ELSE

s = SIN(x); c = COS(x); Cot = c/s END IF

END FUNCTION Cot

This piece of code would be provided alongside a main program. Whenever there is any reference to Cot in the main program (or for that matter anywhere else in the program as a whole, including other functions) this function will be entered and the cotangent will be calculated. When the END FUNCTION statement is reached, control returns back to wherever the reference to Cot was made.

With the function Cot defined as above, references to cot must have one argument, and the argument must have a real value. This argument is called x within the function, but elsewhere a reference to cot can have as its argument the name of any variable, or a constant, or an expression. The name x as it appears above is an arbitrary choice, and x is called a “dummy” argument, because it stands in for whatever “actual” argument will be supplied when the function is invoked. Valid invocations of Cot (occurring in the main program, for example) are the expressions

Cot(y) Cot(0.1)

Cot(pi – SQRT(q**2 + p**2)) or even

Cot(alpha + ATAN((Cot(beta)+Cot(gamma))) the Cot function being called three times by the last example.

It is fundamental to understand clearly the distinction between “dummy” arguments and “actual”

arguments. A dummy argument is a variable with a name local to the function itself, and should be declared at the start of the function along with other variables used in the calculation of the function’s value. An

“actual” argument is supplied at execution time when the function is referred to elsewhere in the program, and the actual argument need not be the name of a variable. The actual arguments in the above examples are

y 0.1

pi – SQRT(q**2 + p**2)

alpha + ATAN((Cot(beta)+Cot(gamma)) beta

gamma.

Leaving aside techniques involving modules (Chapter 10), it should be considered that a function’s arguments are the only way in which data can enter it. Except as arguments, a function does not have access to data items used elsewhere in the program. Conversely, data items used within a function (except the arguments) are internal to the function and are unknown to the rest of the program, even if the names of variables in the function and variables elsewhere have similar names. Variables internal to the function should be declared by type declaration statements within it.

In the example of the Cot function above, the first statement FUNCTION Cot(x)

declares that this is the start of a subprogram that is an external function and it gives it the name Cot. (The use of an initial capital is a suggested convention, not a necessity. COT or cot would have the same effect.) After the name, (x) means that the function has a single argument that is being given the local (“dummy”) name x.

After the FUNCTION statement is a set of declarative statements that set up the necessary variables. First we have

REAL :: Cot

and this is needed because a function (unlike a subroutine) has a value in its own right, and Cot is intended to have a value of REAL type.

As a matter of fact, there is another way of specifying that the function Cot has a real value: the FUNCTION statement itself may be prefixed by the keyword REAL as in

REAL FUNCTION Cot (x)

The function name does not then need to appear in a type declaration statement. Corresponding prefixes can be used also for function values of other types, e.g.

INTEGER FUNCTION Nfactorial(n) COMPLEX FUNCTION Gamma(z)

CHARACTER(20) FUNCTION All_Upper_Case(string) LOGICAL FUNCTION Cone(z)

DOUBLE PRECISION FUNCTION Finetune(p, q)

The function type is never mentioned, however, in END FUNCTION statements.

Since it is not necessary to use type-prefixed FUNCTION statements, and sometimes it can be cumbersome to do so (in Chapter 11 we shall meet other prefixes!), the practice is not recommended.

The next statement in cot,

REAL, INTENT(IN) :: x

declares that the variable x, which is the argument of the function, is of real type. It also specifies what is known as an INTENT attribute for x. The INTENT of an argument is whether it represents information flowing into the procedure or out of it. In this example, it is obvious that the former is intended, i.e. the function receives the value of x when the function is invoked. Later, however, we shall come across cases of external procedures that use arguments as a way of passing information out. The IN above means that information goes in, and if it were going out we would have INTENT (our). It is also possible to have INTENT (IN our) for an argument that can be used to pass information either way.

At this stage it will seem rather pointless to declare the INTENT of any argument, because any argument can be used to pass data either in or out, or both, if the INTENT attribute is omitted entirely. Instead of the above statement we could have simply

REAL :: x

The specification of the INTENT attribute is optional in almost all cases. It is for the sake of more advanced applications, when the INTENT specification can be useful to the compiler, and for the sake of program clarity, that it is recommended that all procedure arguments should have specified INTENT.

The remainder of the function Cot is straightforward to understand. The final declarative statement, REAL :: s, c

affirms that s and c are variables of REAL type. The executable statements IF (x==0.0) THEN

WRITE (*,*) "Error: cotangent function called with &

&zero argument"

Cot = HUGE(x) ELSE

s = SIN(x); c = cos(x); cot = c/s END IF

calculate the required cotangent, and assign a value to Cot accordingly. It is obligatory that a value be given to Cot. Finally we have

END FUNCTION Cot

which ends the subprogram and, on execution, returns control to the point in the main program or elsewhere from which cot was invoked.

Instead of

END FUNCTION Cot

the statement can have the abbreviated form END FUNCTION or even just END.

It has to be understood that the data type of a function’s name is the type of its value, and this is not necessarily the same type as its argument or arguments. For example, a logical function cone could have a complex argument, the function value being true or false according to whether the complex argument z has a real part of greater or lesser magnitude than its imaginary part:

FUNCTION Cone(z) LOGICAL :: Cone

COMPLEX, INTENT(IN) :: z Cone = .FALSE.

IF (ABS(REAL)z) )>ABS(AIMAG(z) ) ) Cone = .TRUE.

END FUNCTION Cone

Functions may have more than one argument, as in FUNCTION Average (r1, r2, r3, r4) REAL :: Average

REAL, INTENT(IN) :: r1, r2, r3, r4 Average = 0.25 * (r1 + r2 + r3 + r4) END FUNCTION Average

In this example the four arguments are all of the same type, and their order is immaterial, but in general it has to be ensured that the proper order of the dummy arguments is adhered to whenever the function is called. This is obvious from the cases of some of the intrinsic functions already met: for example CMPLX(a,b) is not the same as CMPLX(b,a).

It is usually best for a function to receive information through its argument and return information through its value, i.e. to have arguments of INTENT (IN) only. But, if argument values are changed within a function, then the new values will be passed back to the program unit from which the function was called.

When arguments are to be changed, it is often more appropriate to use not a function, but the other type of procedure, namely a subroutine.

Statement functions

Within a program unit it is possible to have a sort of simplified one-line function that can be used only within that program unit. It is specified by a non-executable statement that might typically look like this:

Fun_name(x, y, z) = x/y + y/z + z/x

i.e. an arbitrary function-name, followed by a bracketed list of arguments, followed after an = by an expression giving the value of the function in terms of the arguments. All arguments and other quantities must be scalars or single elements of arrays. Here is another example:

Croot (a) = a**(1.0/3.0) ! Cube Root Function

Such a statement is known as a “statement function” and must appear before any of the executable statements. Its use is not recommended.

There is one final point that needs to be made about functions. A function may involve ancillary variables (such as s and c in Cot above) that are not arguments. If the function is called repeatedly as the program executes, such variables are not “remembered” from one call to the next. Until we meet the attribute SAVE in Chapter 11, and ways of “pooling” data in modules (Chapter 10), all local variables within functions (and also within subroutines) must be regarded as strictly temporary. Anything that a function calculates must be recalculated afresh each time the function is called.

7.2 Exercises 7.A 7.A

1

Write a function to calculate the volume of a sphere given its radius.

7.A 2

Write a complex function to calculate the inverse sine of its (complex) argument.

7.A 3

Write as simple a function as you can to calculate the sine of an angle by adding up the terms of the series

until additional terms do not seem to make much difference. Do not use any Fortran intrinsic functions, and do not use the power operator (**).

7.A 4

The escape velocity from a star or planet is (2GM)1/2/R where G is equal to 6.673 × 10–11Nm–2kg–2. Write a function that calculates the escape velocity as a fraction of the velocity of light c = 299792 kms

1. The function must include a logical argument of INTENT (our) which is returned false if the object is a Black Hole.

7.A 5

Write a function called Ind_int_arcsin to calculate the indefinite integral of the inverse sine function using the equation

7.A 6

The “radiation length” LR for a material of atomic weight A and atomic number Z is given by

and the value of a is 1/137.036. Express the radiation length as a Fortran function.

7.A 7

A logarithm to base 10 can be computed approximately by the formula

where t = (x–1)/(x+1), and a1 = 0.86304, a3 = 0.36415, as long as x is within the range (10)–1/2 ≤ x ∞ (10)1/2.

Use this to calculate the logarithm to base 10 of any positive number. The square root of 10 is 3.

162277660 and its inverse, of course, is 0.3162277660. Your function should be as fast as possible and should not defeat the object of the exercise by calling any of the Fortran intrinsic functions or using the power operator (**).

7.A 8

Try to write an integer square root function, i.e. calculating the square root of an argument that can be assumed to be a perfect square. It should not rely on real-number arithmetic and should be faster (and more reliable) than using INT(SQRT(I))!

7.

A9

Write a function to calculate the natural logarithm of any positive real number. Use the approximation where the “a”s are as follows:

This approximation is valid in the range 0 ≤ x ≤ 1 so it can be used to get the logarithm of a number between 1 and 2. To calculate the logarithm of a number below 1, use ln(y) = –ln(1/y). To calculate the logarithm of a number greater than 2, use the relation ln(y) = ln(2) + ln(y/2), dividing the number by 2 repeatedly until we have a number in the right range for the series approximation above and using ln(2)

= 0.693147180559945.

7.A 10

Using RANDOM, write an integer function that will simulate the throw of two dice, adding their results. If a double is thrown, an extra throw is allowed in addition.

7.3 Subroutines

Subroutines are simpler than functions. While a function has a value, and is called by any mention of its name, a subroutine is called only by a special kind of statement, the CALL statement, as in the main program

PROGRAM Process CALL Read_Data CALL Analyze

CALL Display_Results

END PROGRAM Process

Read_Data, Analyze, and Display_Results are the names of subroutines to which execution is directed in turn by the CALL statements. They do not themselves have values and, in this example, they do not have arguments either. A subroutine can be more or less any block of code, not necessarily calculating anything in particular. At the end of it, control returns back to the calling program at the point immediately following the CALL statement. In the above example three subroutines are called from the main program, but in general any subroutine may be called from anywhere in the program as a whole, including functions and other subroutines.

Information may be passed into and/or out of a subroutine through arguments, as in:

PROGRAM Process

REAL :: x(100), y(100), xprime(100), yprime(100) LOGICAL :: readcheck

CALL Read_Data(x, y, readcheck)

IF (readcheck) CALL Analyze (x, y, xprime, yprime) CALL Display_Results (xprime, yprime, readcheck) END PROGRAM Process

This illustrates, incidentally, that procedure arguments may be arrays. A subroutine is written very much like a main program except that it usually has arguments after its name and it begins with a SUBROUTINE statement. The above program could call the subroutine

SUBROUTINE Display_Results (a, b, go) REAL, INTENT(IN) :: a(100), b(100) LOGICAL, INTENT(IN) :: go

IF (.NOT.go) THEN

WRITE (*,*) "The data was not input correctly"

ELSE . . .

(Display a scatter diagram of a against b) .

. . END IF

END SUBROUTINE Display_Results

Here, as in a function, we have dummy arguments (a, b and go) whose names are local to the subroutine and need not be the same as the names of the actual arguments in the calling program. The dummy arguments here are associated with the actual arguments xprime, yprime, and readcheck by their corresponding positions in the argument lists in the CALL and SUBROUTINE statements. The arrays xprime and yprime have the same shapes as the dummy arguments a and b, i.e. they are rank-one arrays of 100 elements.

Although other possibilities exist, and these will be explained in Chapter 11, for the time being it can be taken that actual arguments and dummy arguments, when they are arrays, must be declared to have matching shapes. It is also possible for procedures to have arguments that are character strings, in which case the actual and dummy argument strings need not be of exactly the same lengths, but the actual argument may not be shorter than the dummy argument.

SUBROUTINE Compare_Strings_20 (string1, string2, equivalent) CHARACTER (20), INTENT (IN) :: string1, string2

LOGICAL, INTENT (OUT) :: equivalent CHARACTER (20) :: s1, s2

equivalent = .FALSE.! Initialization

! Sees if two character strings are equivalent to one

! another, disregarding the distinction between upper-case

!and lower-case letters.

! Work on s1 and s2, leaving string1 and string2 unchanged, s1 = string1; s2 = string2

! The matter is immediately settled if there are different

!numbers of trailing banks.

IF (LEN(TRIM(s1)) /= LEN(TRIM(s2))) RETURN

! Replace all upper-case letter case by the corresponding lower-case ones

DO n = 1, 20

j1 = IACHAR(s1(n:n)); j2 = IACHAR(s2(n:n)) IF (j1>64.AND.j1<91) THEN

j1 = j1 + 32

s1(n:n) = ACHAR(j1) END IF

IF (j2>64.AND.j2<91) THEN j2 = j2 + 32

s2(n:n) = ACHAR(j2) END IF

END DO

IF (s1==s2) equivalent = .TRUE.

END SUBROUTINE Compare_Strings_20

In this example, two of the arguments are 20-character strings. When the subroutine is called, the corresponding arguments must also be character strings each containing at least 20 characters, and their leftmost 20 characters are transmitted to the subroutine as the values of string1 and string2. The subroutine would be called by a statement such as

CALL Compare_Strings_20(teststring,&

"North Dakota ", check)

as long as teststring is the name of a sufficiently long character string and is defined when the call is made, the second argument has enough trailing blanks to make at least 20 characters, and check is the name of a logical variable.

The above subroutine includes a new type of statement, RETURN. The RETURN statement, which may occur in any subroutine or function, has the same effect as jumping down to the END FUNCTION or

END SUBROUTINE statement and returning immediately to where the procedure was called from in the calling program. In the case of a function, though, it has to be remembered that the function must have been given a value before you return from it.

Although the use of a RETURN statement can sometimes be handy, it is never necessary, as execution control constructs can be used so that the actual return always takes place at the END FUNCTION or END SUBROUTINE statement that finishes the procedure. It is therefore suggested that the RETURN statement be avoided.

There are a number of commonsense restrictions on the way that actual arguments may be associated with dummy arguments in subroutines and in functions. A variable that is an actual argument may be accessible within the subroutine, not only via the corresponding dummy argument, but also in some other way: this could happen through host or use association in relation to modules (Chapter 10) or simply via an additional argument. In such circumstances, the value of the variable may only be altered within the procedure via the dummy argument.

If there is any overlap among the actual arguments given to a procedure, then the overlapping bits may not be defined or redefined within the procedure. An example is a subroutine with two arguments, both of which are tenelement arrays, called by the statement

CALL MIX (x(1:10), x(6:15))

Because the arguments overlap with the elements x (6:10), these particular elements may not be redefined within MIX. Similar comments would also apply if we were talking about overlapping substrings of characters. As another example of the same sort of thing, consider

SUBROUTINE Product (a, b, ab) REAL :: a, b, ab

ab = a * b

END SUBROUTINE Product This is all very well, but

CALL Product (alpha, beta, alpha)

is illegal because the dummy arguments a and ab are here being made to overlap (in alpha) and so it is illegal for the subroutine to redefine either of them (specifically, ab). However,

CALL Product (alpha, alpha, beta)

is legal because, although the first two arguments are being made to overlap, the subroutine does not attempt to redefine either a or b. Declarations of INTENT help to make things clearer:

SUBROUTINE Product (a, b, ab)

REAL, INTENT (IN) :: a, b REAL, INTENT (OUT) :: ab ab = a * b

END SUBROUTINE Product and the illegality of

CALL Product (alpha, beta, alpha)

can then be interpreted in terms of the rule that an INTENT (IN) argument must remain unchanged by the procedure.

ENTRY

Usually a procedure is invoked by the name supplied in its first statement, the FUNCTION or SUBROUTINE statement, and it starts executing from that statement; but it is possible to allow a procedure to be entered at different points under different names and with different arguments. This is done by inserting an ENTRY statement at each alternative entry point. A silly example is:

SUBROUTINE Powers(x) REAL:: x ; STOP

ENTRY Fifth (x) ; x=x**5 ; RETURN ENTRY Fourth (x) ; x=x**4 ; RETURN ENTRY Cube (x) ; x=x**3 ; RETURN ENTRY Square (x) ; x=x**2 ; RETURN END SUBROUTINE Powers

This is not intended to be called at all under the name Powers, but by statements like CALL Fifth(y) or CALL Fourth(z) that will return with y or z raised to the power 5 or 4. In general an ENTRY statement consists simply of the keyword ENTRY followed by the name by which it will be called, followed by a

This is not intended to be called at all under the name Powers, but by statements like CALL Fifth(y) or CALL Fourth(z) that will return with y or z raised to the power 5 or 4. In general an ENTRY statement consists simply of the keyword ENTRY followed by the name by which it will be called, followed by a

In document Fortran 95 (Page 123-137)