function

advertisement
Outline of Fortran 90 Topics
•
•
•
•
•
•
•
•
•
•
•
•
•
Overview of Computing (Prog. in Fortran 90/95, chapt 1-4)
– Computer Organization
– Languages
– Problem Solving
Data, Part 1 (chapt 5-6, 10)
– Fortran 90 Character Set
– Variables
– Numeric Data Types
Numeric Expressions
Data, Part 2: Logicals and Logical Expressions (chapt 6, 11)
Control Structures, Part 1: Branching (IF-THEN) (chapt 12)
Data, Part 1B: More Detail About Integers
Control Structures, Part 2: Repetition (DO) (chapt 13)
Data, Part 3: 1D Arrays (chapt 7)
Procedures
– Functions
∗ Intrinsic (chapt 17)
∗ User-Defined (chapt 14)
– Subroutines (chapt 15)
Data, Part 4: Multidimensional Arrays
Data, Part 5: Characters
Data, Part 6: Derived Data Types
Input/Output
– Formatting
– Files
Note: this is roughly how this part of the semester will go.
1
Functions in Mathematics
“In mathematics a function is a rule that assigns to each element
of a set an element of the same or of another set.” (Bers & Karal,
Calculus, 2nd ed, Holt, Reinhart & Winston, 1976, p. 50.)
So, for example, if we have a function
f (x) = x + 1
then we know that
f (−2.5)
f (−2)
f (−1)
f (0)
f (+1)
f (+2)
f (+2.5)
=
=
=
=
=
=
=
...
−2.5
−2
−1
0
+1
+2
+2.5
...
+
+
+
+
+
+
+
1
1
1
1
1
1
1
=
=
=
=
=
=
=
−1.5
−1
0
+1
+2
+3
+3.5
=
=
=
=
=
=
=
2.5
2
1
0
1
2
2.5
Likewise, if we have a function
a(y) = |y|
then we know that
a(−2.5)
a(−2)
a(−1)
a(0)
a(+1)
a(+2)
a(+2.5)
=
=
=
=
=
=
=
...
| −2.5
| −2
| −1
|
0
| +1
| +2
| +2.5
...
|
|
|
|
|
|
|
We refer to the thing inside the parentheses – in the first example it’d
be x, and in the second example y – as the argument (or sometimes
the parameter) of the function.
2
Functions in Fortran 90
From time to time in our programs we’ve seen things like this:
IF (money_owed_or_received <
&
&
amount_cannot_be_negative) THEN
money_owed = ABS(money_owed_or_received)
PRINT *,"You owe the goverment $",
&
&
NINT(money_owed),"."
ELSE
money_received = money_owed_or_received
PRINT *,"The government owes you $", &
&
NINT(money_received),"."
END IF
So, what do ABS and NINT do?
ABS is a function that calculates the absolute value of its argument.
It is the Fortran 90 analogue of the mathematical function a that we
just looked at. So:
...
ABS(-2.5)
ABS(-2)
ABS(-1)
ABS(0)
ABS(1)
ABS(2)
ABS(2.5)
...
→
→
→
→
→
→
→
2.5
2
1
0
1
2
2.5
(Note: here → denotes “evaluates to.”)
Similarly, NINT calculates the nearest integer to the given real value,
by rounding.
An important distinction between a mathematical function and a Fortran 90 function: a mathematical function is simply a definition,
while a Fortran 90 function does stuff. More on this shortly.
3
A Quick Look at ABS
% cat abstest.f90
PROGRAM abstest
IMPLICIT NONE
PRINT *, "ABS(-2.5) = ",
PRINT *, "ABS(-2)
= ",
PRINT *, "ABS(-1)
= ",
PRINT *, "ABS( 0)
= ",
PRINT *, "ABS( 1)
= ",
PRINT *, "ABS( 2)
= ",
PRINT *, "ABS( 2.5) = ",
END PROGRAM abstest
% f90 -o abstest abstest.f90
% abstest
ABS(-2.5) =
2.500000
ABS(-2)
=
2
ABS(-1)
=
1
ABS( 0)
=
0
ABS( 1)
=
1
ABS( 2)
=
2
ABS( 2.5) =
2.500000
ABS(-2.5)
ABS(-2)
ABS(-1)
ABS( 0)
ABS( 1)
ABS( 2)
ABS( 2.5)
(An interesting property of ABS: if its argument is an INTEGER, it
returns an INTEGER result, but if its argument is a REAL, it returns
a REAL result. More on this later.)
Jargon: in programming, the use of a function in an expression is
referred to as an invocation, or more colloquially as a call. We say
that the statement
PRINT *, ABS(-2)
invokes or calls the function ABS; the statement passes an argument
of -2 to the function; the function ABS returns a value of 2.
4
Domain & Range
In mathematics, the set of numbers that can be arguments into a
given function is referred to as the domain of that function.
Similarly, the set of numbers that can be results of a given function
is referred to as the range of that function.
For example, in the case of the function
f (x) = x + 1
we define the domain to be the set of real numbers (sometimes denoted <), which means that the x in f (x) can be any real number.
Likewise, we also define the range to be the set of real numbers, because for every real number y there is some real number x such that
f (x) = y.
On the other hand, for a function
1
x−1
the domain cannot include 1, because
q(x) =
1
1
=
1−1
0
which is undefined. So the domain might be < − {1}.
q(1) =
In that case, the range of q would be the set of all real numbers except
0, because there’s no real number y such that 1/y is 0.
(Note: if you’ve taken calculus, you’ve seen that, as y gets arbitrarily
large, 1/y approaches 0 as a limit – but “gets arbitrarily large” is not
a real number, and neither is “approaches 0 as a limit.”)
Fortran 90 has concepts that are analogous to the mathematical domain and range: argument type and return type. More on these
shortly.
5
Intrinsic Functions in Fortran 90
Fortran 90 has a bunch of functions built into the language. These
functions are referred to as intrinsic functions, where intrinsic means
“built into the language.” A few examples:
Function name
ABS(x)
SQRT(x)
EXP(x)
LOG(x)
LOG10(x)
SIN(x)
COS(x)
TAN(x)
CEILING(x)
FLOOR(x)
HUGE(x)
TINY(x)
Math Name
Value Example
absolute value
|x|
ABS(-1.0)
1/2
square root
x
SQRT(2.0)
x
exponential
e
EXP(1.0)
natural logarithm
ln x
EXP(2.718...)
common logarithm log x LOG10(100.0)
sine
sin x SIN(3.14...)
cosine
cos x COS(3.14...)
tangent
tan x TAN(3.14...)
least integer ≥ x
dxe
CEILING(2.5)
greatest integer ≤ x bxc
FLOOR(2.5)
largest possible value of same type as x
smallest positive value of same type as x
→
→
→
→
→
→
→
→
→
→
1.0
1.414...
2.718...
1.0
2.0
0.0
-1.0
0.0
3
2
You’ll find an exhaustive list of all of Fortran 90’s intrinsic functions
(more than 100 of them) in Programming in Fortran 90/95, Chapter
17, pages 205-208.
6
Function Arguments
In mathematics, the argument of a function can be:
• a number:
•
•
•
•
f (5) = 5 + 1 = 6
a variable:
f (z) = z + 1
an arithmetic expression:
f (5 + 7) = (5 + 7) + 1 = 12 + 1 = 13
another function:
f (a(w)) = |w| + 1
any combination of these; i.e., any general expression whose
value is in the domain of the function:
f (3a(5w + 7)) = 3 (|5w + 7|) + 1
Likewise, in Fortran 90 the argument of a function can be any
nonempty expression, including an expression containing a function,
as long as the expression that constitutes the argument evaluates to
the appropriate type.
7
Function Argument Example
% cat funcargs.f90
PROGRAM funcargs
IMPLICIT NONE
REAL,PARAMETER :: pi = 3.1415926
REAL :: angle_in_radians
PRINT ’(A,F9.7,A,F10.7)’,
&
&
"COS(", 1.5707963, ") = ", COS(1.5707963)
PRINT ’(A,F9.7,A,F10.7)’, "COS(", pi, ") = ", &
&
COS(pi)
PRINT ’(A)’, "Enter an angle in radians:"
READ *, angle_in_radians
PRINT ’(A,F10.7,A,F10.7)’,
&
&
"COS(", angle_in_radians, ") = ",
&
&
COS(angle_in_radians)
PRINT ’(A,F10.7,A,F10.7)’,
&
&
"ABS(COS(", angle_in_radians, ")) = ",
&
& ABS(COS(angle_in_radians))
PRINT ’(A,F10.7,A,F10.7)’,
&
&
"COS(ABS(", angle_in_radians, ")) = ",
&
& COS(ABS(angle_in_radians))
PRINT ’(A,F10.7,A,F10.7)’,
&
&
"ABS(COS(2.0 * ", angle_in_radians, ")) = ", &
&
ABS(COS(2.0 * angle_in_radians))
PRINT ’(A,F10.7,A,F10.7)’,
&
&
"ABS(2.0 * COS(", angle_in_radians, ")) = ", &
&
ABS(2.0 * COS(angle_in_radians))
PRINT ’(A,F10.7,A,F10.7)’,
&
&
"ABS(2.0 * COS(1.0 / 5.0 * ",
&
&
angle_in_radians, ")) = ",
&
&
ABS(2.0 * COS(1.0 / 5.0 * angle_in_radians))
END PROGRAM funcargs
% f90 -o funcargs funcargs.f90
% funcargs
COS(1.5707963) = 0.0000001
COS(3.1415925) = -1.0000000
Enter an angle in radians:
-3.1415926
COS(-3.1415925) = -1.0000000
ABS(COS(-3.1415925)) = 1.0000000
COS(ABS(-3.1415925)) = -1.0000000
ABS(COS(2.0 * -3.1415925)) = 1.0000000
ABS(2.0 * COS(-3.1415925)) = 2.0000000
ABS(2.0 * COS(1.0 / 5.0 * -3.1415925)) = 1.6180340
8
Using Functions
Functions are used in expressions in exactly the same ways that variables are used. For example, a function call can be used on the right
hand side of an assignment:
REAL :: theta, cos_theta
theta = 3.1415926 / 4.0
cos_theta = COS(theta)
A function call can also be used in an expression in list-directed
output:
PRINT *, 2.0
PRINT *, COS(theta) ** 2.0
And, since any expression can be used as some function’s argument,
a function call can also be used as an argument to another function:
REAL,PARAMETER :: pi = 3.1415926
PRINT *, 1 + COS(ASIN(SQRT(2.0)/2.0) + pi)
However, most function calls cannot be used in initialization:
! ILLEGAL ILLEGAL ILLEGAL
REAL :: theta, cos_theta = COS(3.1415926)
! ILLEGAL ILLEGAL ILLEGAL
Nor can most function calls be used in named constant declaration:
! ILLEGAL ILLEGAL ILLEGAL
REAL,PARAMETER :: cos_theta = COS(3.1415926)
! ILLEGAL ILLEGAL ILLEGAL
As a general rule, it’s best not to try to use function calls in initializations or in named constant declarations.
9
Function Use Example
% cat funcuse.f90
PROGRAM funcuse
IMPLICIT NONE
REAL,PARAMETER :: pi = 3.1415926
!
REAL,PARAMETER :: sin_pi = SIN(3.1415926) ! ILLEGAL
!
REAL :: sin_pi = SIN(pi)
! ILLEGAL
!
REAL :: sin_pi = SIN(3.1415926) ! ILLEGAL
REAL :: theta, sin_pi, sin_theta
theta
= 3.0 * pi / 4
sin_pi
= SIN(pi)
sin_theta = SIN(theta)
PRINT *, "2.0
= ", 2.0
PRINT *, "pi
= ", pi
PRINT *, "theta
= ", theta
PRINT *, "SIN(pi)
= ", SIN(pi)
PRINT *, "sin_pi
= ", sin_pi
PRINT *, "SIN(theta) = ", SIN(theta)
PRINT *, "sin_theta = ", sin_theta
PRINT *, "SIN(theta) ** (1.0/3.0) = ", &
&
SIN(theta) ** (1.0/3.0)
PRINT *, "1 + SIN(ACOS(1.0))
= ", &
&
1 + SIN(ACOS(1.0))
PRINT *, "SIN(ACOS(1.0))
= ", &
&
SIN(ACOS(1.0))
PRINT *, "SQRT(2.0)
= ", &
&
SQRT(2.0)
PRINT *, "SQRT(2.0) / 2
= ", &
&
SQRT(2.0) / 2
PRINT *, "ACOS(SQRT(2.0)/2.0)
= ", &
&
ACOS(SQRT(2.0)/2.0)
PRINT *, "SIN(ACOS(SQRT(2.0)/2.0)) = ", &
&
SIN(ACOS(SQRT(2.0)/2.0))
END PROGRAM funcuse
% f90 -o funcuse funcuse.f90
% funcuse
2.0
=
2.000000
pi
=
3.141593
theta
=
2.356194
SIN(pi)
=
1.5099580E-07
sin_pi
=
1.5099580E-07
SIN(theta) =
0.7071068
sin_theta =
0.7071068
SIN(theta) ** (1.0/3.0) =
0.8908987
1 + SIN(ACOS(1.0))
=
1.000000
SIN(ACOS(1.0))
=
0.0000000E+00
SQRT(2.0)
=
1.414214
SQRT(2.0) / 2
=
0.7071068
ACOS(SQRT(2.0)/2.0)
=
0.7853982
SIN(ACOS(SQRT(2.0)/2.0)) =
0.7071068
10
Evaluation of Functions in Expressions
When a function call appears in an expression – for example, on the
right hand side of an assignment statement – the function is evaluated just before its value is needed according to precedence order.
For example, if x and y are REAL variables, and y has already been
assigned the value -10.0, then the assignment statement
x = 1 + 2.0 * 8.0 + ABS(y) / 4.0
is evaluated like so:
x = 1 + 2.0 * 8.0 +
ABS(y)
/ 4.0 ⇒
ABS(y)
/ 4.0 ⇒
x =
1
+
16.0
+
x =
1
+
16.0
+ ABS(-10.0) / 4.0 ⇒
x =
1
+
16.0
+
x =
1
+
16.0
+
2.5
⇒
x = 1.0 +
16.0
+
2.5
⇒
+
2.5
⇒
x =
x =
17.0
10.0
/ 4.0 ⇒
19.5
So, the variable x is ultimately assigned the value 19.5.
11
Functions with Multiple Arguments
In mathematics, we sometimes have functions that have multiple arguments:
h(x, y) = xy + 2x + 3y + 5
In this case, we know that
h(−2.5, −1.5)
h(−2, −0.5)
h(−1, 1.25)
h(0, 0)
=
=
=
=
(−2.5)(−1.5)
(−2)(−0.5)
(−1)(1.25)
(0)(0)
+
+
+
+
(2)(−2.5)
(2)(−2)
(2)(−1)
(2)(0)
+ (3)(−1.5) + 5 = −0.75
+ (3)(−0.5) + 5 = +0.5
+ (3)(1.25) + 5 =
0
+ (3)(0)
+ 5 = +5
Here, we define the domain of the first argument of h to be the set of
all real numbers <, and likewise we define the domain of the second
argument of h to be the set of all real numbers <, so the domain of h
as a whole is < × <.
Since the result of h is a single real value, we define the range of h
to be <, and we denote the mapping as
h : < × < 7→ <
Similarly, in Fortran 90 we have intrinsic functions with multiple
arguments. Examples include:
Function name
MIN(x,y)
MAX(x,y)
MOD(x,y)
BTEST(x,y)
Math Name
minimum
maximum
remainder
bit test
Math Value
if (x < y) then x else y
if (x > y) then x else y
x - INT(x / y) · y
.TRUE. if the yth bit of x is 1
Functions with multiple arguments can be used in exactly the same
ways as functions with a single argument.
The order of the arguments in the function call is extremely important.
12
Intrinsic Functions Are Not Enough
Often, we have a particular kind of value that we need to calculate
over and over again, under a variety of circumstances.
For example, in our income tax program (Programming Project #3),
we had to calculate the total tax, based on the taxable income as
well as on the base tax, lower bound of income and tax rate for the
appropriate tax bracket:
&
&
&
&
&
&
&
&
&
&
&
&
&
&
&
&
IF (filing_status == single_status) THEN
IF (taxable_income > no_income) .AND. &
(taxable_income <= lower_bound_single1) THEN
total_tax =
&
base_tax_single1 + &
tax_rate_single1 * &
(taxable_income - lower_bound_single1)
ELSE IF (taxable_income > lower_bound_single1) .AND. &
(taxable_income <= lower_bound_single2) THEN
total_tax =
&
base_tax_single2 + &
tax_rate_single2 * &
(taxable_income - lower_bound_single2)
...
END IF
ELSE
IF (taxable_income > no_income) .AND. &
(taxable_income <= lower_bound_married1) THEN
total_tax =
&
base_tax_married1 + &
tax_rate_married1 * &
(taxable_income - lower_bound_married1)
ELSE IF (taxable_income > lower_bound_married1) .AND. &
(taxable_income <= lower_bound_married2) THEN
total_tax =
&
base_tax_married2 + &
tax_rate_married2 * &
(taxable_income - lower_bound_married2)
...
END IF
END IF
Notice that the assignment
&
&
&
total_tax =
&
base_tax_x + &
tax_rate_x * &
(taxable_income - lower_bound_x)
keeps coming up over and over again.
13
User-Defined Functions
So, it’d be nice to replace the assignment
&
&
&
total_tax =
&
base_tax_x + &
tax_rate_x * &
(taxable_income - lower_bound_x)
with a function call that would calculate the total tax for us:
&
&
&
total_tax =
&
total_tax_for_bracket(
&
taxable_income, base_tax_married2, &
tax_rate_married2, lower_bound_married2)
Obviously, the designers of Fortran 90 weren’t able to anticipate that
we might want a function to do this – as well as a zillion other things
that we might need functions to do – so there’s no intrinsic function
to calculate something completely application-specific such as total
tax.
Instead, we as users of Fortran 90 are going to have to define our
own function to do it:
REAL FUNCTION total_tax_for_bracket (
&
&
taxable_income, base_tax, tax_rate, &
&
lower_bound)
IMPLICIT NONE
REAL :: taxable_income, base_tax, &
&
tax_rate, lower_bound
total_tax_for_bracket = &
&
base_tax +
&
&
tax_rate *
&
&
(taxable_income - lower_bound)
END FUNCTION total_tax_for_bracket
Because we as users of Fortran 90 define this kind of function our
own personal selves, this kind of function is referred to as a userdefined function.
14
General Form of
User-Defined Function Definitions
In general, the definition of a user-defined function looks an awful
lot like a program, except that:
1. The keyword PROGRAM is replaced by the keyword FUNCTION.
So, the function has a FUNCTION statement and an END FUNCTION statement, which are analogous to a program’s PROGRAM
statement and END PROGRAM statement.
2. In the FUNCTION statement, immediately before the keyword
FUNCTION is a data type that declares the return type, which is
the type of the value that the function will return and (for now)
must be a basic scalar type (e.g., INTEGER, REAL).
3. In the FUNCTION statement, immediately after the function
name is a list of arguments, enclosed in parentheses and separated by commas (just as in a mathematical function).
4. In the function’s declaration section, the arguments must be declared exactly as if they were variables. Good programming
style is to declare local named constants, then arguments, then
local variables (though in principle this order is not required).
5. In the body of the function, the function name is used as if it
were a variable, and it must be assigned the value that the function is to return, so it’s referred to as the return variable.
returntype FUNCTION funcname ( arg1, arg2, ... )
IMPLICIT NONE
localconst1type,PARAMETER :: localconst1
localconst2type,PARAMETER :: localconst2
...
arg1type, arg1attributes :: arg1
arg2type, arg2attributes :: arg2
...
localvar1type, localvar1attributes :: localvar1
localvar2type, localvar2attributes :: localvar2
...
[ function body: does stuff ]
funcname = returnvalue
END FUNCTION funcname
15
User-Defined Function Example
Here’s a definition of a user-defined function.
REAL FUNCTION cube_root (original_value)
IMPLICIT NONE
REAL :: original_value
REAL,PARAMETER :: cube_root_power = 1.0 / 3.0
cube_root = original_value ** cube_root_power
END FUNCTION cube_root
What can we say about this user-defined function?
1.
2.
3.
4.
Its name is cube root.
Its return type is REAL.
It has one argument, whose type is REAL.
It calculates and returns the cube root of the incoming argument.
So, cube root calculates the cube root of a REAL argument and
returns a REAL result whose value is the cube root of the argument.
Does the name of a user-defined function have to be meaningful?
Absolutely not; you could easily have a function called square root
that always returns 12. But that’d be REALLY REALLY DUMB,
and you’d get a VERY BAD GRADE.
Jargon: for a particular user-defined function, the set of statements
from the FUNCTION statement through the END FUNCTION statement is referred to as that particular function unit.
Likewise, the set of statements from the PROGRAM statement through
the END PROGRAM statement is referred to as the program unit.
16
Using a User-Defined Function: Example #1
% cat cube_root_scalar.f90
PROGRAM cube_root_scalar
IMPLICIT NONE
REAL :: input_value1, cube_root_value1
REAL :: input_value2, cube_root_value2
REAL :: input_value3, cube_root_value3
REAL,EXTERNAL :: cube_root ! Function return type
PRINT *, "What 3 real numbers would you like ", &
&
"the cube roots of?"
READ *, input_value1, input_value2, input_value3
cube_root_value1 = cube_root(input_value1)
cube_root_value2 = cube_root(input_value2)
cube_root_value3 = cube_root(input_value3)
PRINT *, "The cube root of ", input_value1, &
&
" is ", cube_root_value1, "."
PRINT *, "The cube root of ", input_value2, &
&
" is ", cube_root_value2, "."
PRINT *, "The cube root of ", input_value3, &
&
" is ", cube_root_value3, "."
END PROGRAM cube_root_scalar
REAL FUNCTION cube_root (original_value)
IMPLICIT NONE
REAL :: original_value
REAL,PARAMETER :: cube_root_power = 1.0 / 3.0
cube_root = original_value ** cube_root_power
END FUNCTION cube_root
% f90 -o cube_root_scalar cube_root_scalar.f90
% cube_root_scalar
What 3 real numbers would you like the cube roots of?
1, 8, 100
The cube root of
1.000000
is
1.000000
.
The cube root of
8.000000
is
2.000000
.
The cube root of
100.0000
is
4.641589
.
Notice this declaration:
REAL,EXTERNAL :: cube root
This declaration tells the compiler that there’s a function called
cube root with a return type of REAL, and that it’s declared external to (outside of) the program unit.
17
User-Defined Function Example #2: Array
% cat cube_root_array.f90
PROGRAM cube_root_array
IMPLICIT NONE
INTEGER,PARAMETER :: numvalues = 5
REAL,DIMENSION(numvalues) :: &
&
input_value, cube_root_value
INTEGER :: i
REAL,EXTERNAL :: cube_root ! Function return type
PRINT *, "What ", numvalues, " real numbers"
PRINT *, " would you like the cube roots of?"
READ *, (input_value(i), i = 1, numvalues)
DO i = 1, numvalues
cube_root_value(i) = cube_root(input_value(i))
END DO
DO i = 1, numvalues
PRINT *, "The cube root of ", &
&
input_value(i), " is ", cube_root_value(i), "."
END DO
END PROGRAM cube_root_array
REAL FUNCTION cube_root (original_value)
IMPLICIT NONE
REAL :: original_value
REAL,PARAMETER :: cube_root_power = 1.0 / 3.0
cube_root = original_value ** cube_root_power
END FUNCTION cube_root
% f90 -o cube_root_array cube_root_array.f90
% cube_root_array
What
5 real numbers
would you like the cube roots of?
1, 8, 100, 125, 400
The cube root of
1.000000
is
1.000000
The cube root of
8.000000
is
2.000000
The cube root of
100.0000
is
4.641589
The cube root of
125.0000
is
5.000000
The cube root of
400.0000
is
7.368063
18
.
.
.
.
.
Reusing User-Defined Functions
via the INCLUDE Statement
In the previous two example programs, the function definitions for
cube root were character-for-character identical.
It’d be a big waste of time to have to type the exact same function
over and over if we wanted to use it multiple times.
So, Fortran 90 allows us to put the function in a completely separate
.f90 source file, and to include it when we’re compiling, by using
an INCLUDE statement.
The general form of the INCLUDE statement is:
INCLUDE ’filename’
or:
INCLUDE "filename"
For example:
INCLUDE ’cube root.f90’
What Does This Mean?
When the compiler encounters an INCLUDE statement, it brings the
contents of the included source file into the primary source file that
it’s currently compiling.
So, the upshot is that the compiler ends up treating the primary
source file exactly as if you had typed the contents of the included
file into it, in the place where the INCLUDE statement is.
This promotes code reuse.
19
INCLUDE Statement Example #1
% cat cube_root_scalarinc.f90
PROGRAM cube_root_scalarinc
IMPLICIT NONE
REAL :: input_value1, cube_root_value1
REAL :: input_value2, cube_root_value2
REAL :: input_value3, cube_root_value3
REAL,EXTERNAL :: cube_root ! Function return type
PRINT *, "What 3 real numbers would you like ", &
&
"the cube roots of?"
READ *, input_value1, input_value2, input_value3
cube_root_value1 = cube_root(input_value1)
cube_root_value2 = cube_root(input_value2)
cube_root_value3 = cube_root(input_value3)
PRINT *, "The cube root of ", input_value1, &
&
" is ", cube_root_value1, "."
PRINT *, "The cube root of ", input_value2, &
&
" is ", cube_root_value2, "."
PRINT *, "The cube root of ", input_value3, &
&
" is ", cube_root_value3, "."
END PROGRAM cube_root_scalarinc
INCLUDE ’cube_root.f90’
% cat cube_root.f90
REAL FUNCTION cube_root (original_value)
IMPLICIT NONE
REAL :: original_value
REAL,PARAMETER :: cube_root_power = 1.0 / 3.0
cube_root = original_value ** cube_root_power
END FUNCTION cube_root
% f90 -o cube_root_scalarinc cube_root_scalarinc.f90
% cube_root_scalarinc
What 3 real numbers would you like the cube roots of?
1, 8, 100
The cube root of
1.000000
is
1.000000
.
The cube root of
8.000000
is
2.000000
.
The cube root of
100.0000
is
4.641589
.
20
INCLUDE Statement Example #2
% cat cube_root_arrayinc.f90
PROGRAM cube_root_arrayinc
IMPLICIT NONE
INTEGER,PARAMETER :: numvalues = 5
REAL,DIMENSION(numvalues) :: &
&
input_value, cube_root_value
INTEGER :: i
REAL,EXTERNAL :: cube_root ! Function return type
PRINT *, "What ", numvalues, " real numbers"
PRINT *, " would you like the cube roots of?"
READ *, (input_value(i), i = 1, numvalues)
DO i = 1, numvalues
cube_root_value(i) = cube_root(input_value(i))
END DO
DO i = 1, numvalues
PRINT *, "The cube root of ", &
&
input_value(i), " is ", cube_root_value(i), "."
END DO
END PROGRAM cube_root_arrayinc
INCLUDE "cube_root.f90"
% cat cube_root.f90
REAL FUNCTION cube_root (original_value)
IMPLICIT NONE
REAL :: original_value
REAL,PARAMETER :: cube_root_power = 1.0 / 3.0
cube_root = original_value ** cube_root_power
END FUNCTION cube_root
% f90 -o cube_root_arrayinc cube_root_arrayinc.f90
% cube_root_arrayinc
What
5 real numbers
would you like the cube roots of?
1, 8, 100, 125, 400
The cube root of
1.000000
is
1.000000
The cube root of
8.000000
is
2.000000
The cube root of
100.0000
is
4.641589
The cube root of
125.0000
is
5.000000
The cube root of
400.0000
is
7.368063
21
.
.
.
.
.
Actual Arguments vs. Formal Arguments
In our cube root examples, we’ve seen function calls that look like
this:
cube root value1 = cube root(input value1)
We say that
• this assignment statement
• calls the user-defined function cube root
• using as its actual argument the variable input value1
• which corresponds to the function definition’s formal argument
original value
• and returns the cube root of
• the value of stored in the variable input value1.
The actual argument is the argument that appears in the call to the
function (for example, in the main program).
The formal argument – which in Fortran 90 is also called the dummy
argument – is the argument that appears in the definition of the function.
Not surprisingly, the mathematical case is the same. In a mathematical function definition like
f (x) = x + 1
if we want the value of
f (1)
then x is the formal argument of the function f , and 1 is the actual
argument.
22
Side Effects
A side effect of a function is something that the function does other
than calculate and return its return value, and that affects something
other than the values of local variables. For example, consider this
function:
INTEGER FUNCTION input_number_of_elements (
&
&
minimum_number_of_elements, &
&
maximum_number_of_elements)
IMPLICIT NONE
INTEGER :: minimum_number_of_elements, &
&
maximum_number_of_elements
INTEGER :: numelts
DO
PRINT *, "How many elements would you like"
PRINT *, " the array to have"
PRINT *, " (between ",
&
&
minimum_number_of_elements, " and ", &
&
maximum_number_of_elements, ")?"
READ *, numelts
IF (numelts <
&
&
minimum_number_of_elements) THEN
PRINT *, "Too few, idiot!"
ELSE IF (numelts > &
&
maximum_number_of_elements) THEN
PRINT *, "Too many, fool!"
ELSE
EXIT
END IF
END DO
input_number_of_elements = numelts
END FUNCTION input_number_of_elements
This function has the side effect of outputting a prompt message to
the user, as well as outputting an error message if needed, and doing
so multiple times, until the user inputs an appropriate value.
Notice that the READ statement inputs into a local variable called
numelts, and at the end of the function the value of numelts
is assigned to the return variable input number of elements,
which makes the program easier to read.
23
Side Effects Example
% cat userarray.f90
PROGRAM userarray
IMPLICIT NONE
INTEGER,PARAMETER :: first_element =
1
INTEGER,PARAMETER :: minnumelements =
1
INTEGER,PARAMETER :: maxnumelements = 100
REAL,DIMENSION(first_element:maxnumelements) &
&
:: element
INTEGER :: number_of_elements
INTEGER :: input_number_of_elements
number_of_elements =
&
&
input_number_of_elements( &
&
minnumelements, maxnumelements)
PRINT ’(A,I3,A)’,
&
&
"The number of elements you plan to input is ", &
&
number_of_elements, "."
END PROGRAM userarray
INCLUDE ’inputnumelts.f90’
% cat inputnumelts.f90
INTEGER FUNCTION input_number_of_elements (
&
&
minimum_number_of_elements, &
&
maximum_number_of_elements)
IMPLICIT NONE
INTEGER :: minimum_number_of_elements, &
&
maximum_number_of_elements
INTEGER :: numelts
DO
PRINT *, "How many elements would you like"
PRINT *, " the array to have"
PRINT *, " (between ",
&
&
minimum_number_of_elements, " and ", &
&
maximum_number_of_elements, ")?"
READ *, numelts
IF (numelts <
&
&
minimum_number_of_elements) THEN
PRINT *, "Too few, idiot!"
ELSE IF (numelts > &
&
maximum_number_of_elements) THEN
PRINT *, "Too many, fool!"
ELSE
EXIT
END IF
END DO
input_number_of_elements = numelts
END FUNCTION input_number_of_elements
% f90 -o userarray userarray.f90
% userarray
How many elements would you like
the array to have
(between
1 and
100 )?
0
Too few, idiot!
How many elements would you like
the array to have
(between
1 and
100 )?
101
Too many, fool!
How many elements would you like
the array to have
(between
1 and
100 )?
50
The number of elements you plan to input is 50.
24
A Function That Doesn’t
Return a Value
So, suppose we wanted, for example, to read in a given number of
values into an array. We might define a function like so:
INTEGER FUNCTION input_elements (
&
element, number_of_elements,
&
minimum_number_of_elements,
&
maximum_number_of_elements)
IMPLICIT NONE
INTEGER :: number_of_elements
INTEGER :: minimum_number_of_elements, &
&
maximum_number_of_elements
REAL,DIMENSION(minimum_number_of_elements:
&
maximum_number_of_elements) ::
&
element
INTEGER :: numelts, e
PRINT *, "What are the ", number_of_elements,
&
" elements of the array?"
READ *, (element(e),
&
&
e = minimum_number_of_elements, &
&
maximum_number_of_elements)
input_elements = 000
END FUNCTION input_elements
&
&
&
&
&
&
What on earth are we going to return?
The best answer is, we’re not going to return anything.
But if we’re not returning anything, it’s not a function.
So, we have a special construct in case we don’t want to return anything: a subroutine.
25
Subroutines
A subroutine is exactly like a function, except that:
1. The keyword FUNCTION is replaced by the keyword SUBROUTINE. So, the subroutine has a SUBROUTINE statement and an
END SUBROUTINE statement, which are analogous to a function’s FUNCTION statement and END FUNCTION statement.
2. A subroutine has no return type and no return variable.
SUBROUTINE input_elements (
&
&
element, number_of_elements, &
&
minimum_number_of_elements, &
&
maximum_number_of_elements)
IMPLICIT NONE
INTEGER :: number_of_elements
INTEGER :: minimum_number_of_elements, &
&
maximum_number_of_elements
REAL,DIMENSION(minimum_number_of_elements:
&
&
maximum_number_of_elements) :: &
&
element
INTEGER :: numelts, e
PRINT *, "What are the ", number_of_elements, &
&
" elements of the array?"
READ *, (element(e),
&
&
e = minimum_number_of_elements, &
&
maximum_number_of_elements)
END SUBROUTINE input_elements
A subroutine is invoked using a CALL statement:
CALL input_elements(element,numelts, &
&
minnumelements,maxnumelements)
Notice that a subroutine has to have side effects to be useful.
The general term for functions and subroutines is procedures. That
is, a procedure is either a function or a subroutine.
26
Why Do We Like Code Reuse?
1. Bug avoidance: Since we don’t have to retype the procedure
from scratch every time we use it, we aren’t constantly making
new and exciting typos.
2. Implementation efficiency: We aren’t wasting valuable programming time ($8 - $100s per programmer per hour) on writing commonly used procedures from scratch.
3. Verification: We can test a procedure under every conceivable
case, so that we’re confident that it works, and then we don’t
have to worry about whether the procedure has bugs when we
use it in a new program.
Why Do We Like User-Defined Procedures?
1. Code Reuse (see above)
2. Encapsulation: We can write a procedure that packages an important concept (e.g., the cube root). That way, we don’t have
to litter our program with cube root calculations. So someone
reading our program will be able to tell immediately that, for
example, a particular statement has a cube root in it, rather than
constantly having to figure out what
x ** (1.0 / 3.00)
means.
3. Modular Programming: If we make a bunch of encapsulations,
we can have our main program simply call a bunch of procedures. That way, it’s easy for someone reading our code to tell
what’s going on in the main program, and then to look at individual procedures to see how they work.
27
Download