The Queen's University of Belfast
Parallel Computer Centre

[Next] [Previous] [Top]

7 Program units


7.1 Program structure

A single Fortran 90 program can be made up of a number of distinct program units, namely procedures (internal, external and module) and modules. An executable program consists of one main program, and any number (including zero) of other program units. It is important to realise that the internal details of each program unit is separate from other units. The only link between units is the interface, where one unit invokes another by name. The key to writing programs through program units is to ensure that the procedure interfaces are consistent.

The following illustrates the relationship between the different types of program units:

Dividing a program into units has several advantages:

7.2 The main program

All programs have one (and only one) main program. A program always begins executing from the first statement in the main program unit, and proceeds from there. The general form of the main program unit is:

PROGRAM [name]

[specification statements]

[executable statements]

...

[CONTAINS

internal procedures]

END [PROGRAM [name]]

The PROGRAM statement marks the beginning of the main program unit while the END PROGRAM statement not only marks the end of the unit but also the end of the program as a whole. The name of the program is optional but advisable. The CONTAINS statement serves to identify any procedures that are internal to the main program unit. (Internal procedures are dealt with later on in this chapter.) When all executable statements are complete, control is passed over any internal procedures to the END statement.

A program can be stopped at any point during its execution, and from any program unit, through the STOP statement:

STOP [label]

where label is an optional character string (enclosed in quotes) which may be used to inform the user why and at what point the program has stopped.

7.3 Procedures

Procedures are a type of program unit, and may be either subroutines or functions. Procedures are used to group together statements that perform a self-contained, well defined task. Both subroutines and functions have the following general form:

procedure name [(argument list)]

[specification statements]

[executable statements]

...

[CONTAINS

internal procedures]

END procedure [name]

where procedure may be either SUBROUTINE or FUNCTION.

There are several different types of procedure:

To use a procedure (regardless of type) requires a referencing statement. Subroutines are invoked by the CALL statement while functions are referenced by name:

CALL name [( argument list )]

result = name [( argument list )]

In both cases control is passed to the procedure from the referencing statement, and is returned to the same statement when the procedure exits. The argument list are zero or more variables or expressions, the values of which are used by the procedure.

7.3.1 Actual and dummy arguments

Procedures are used to perform well defined tasks using the data available to them. The most common way to make data available to a procedure is by passing it in an argument list when the procedure is referenced.

An argument list is simply a number of variables or expressions (or even procedure names - see later). The argument(s) in a referencing statement are called actual arguments, while those in the corresponding procedure statement are call dummy arguments. Actual and dummy argument are associated by their position in a list, i.e the first actual argument corresponds to the first dummy argument, the second actual argument with the second dummy argument, etc. The data type, rank, etc. of actual and dummy arguments must correspond exactly.

When a procedure is referenced data is copied from actual to dummy argument(s), and is copied back from dummy to actual argument(s) on return. By altering the value of a dummy argument, a procedure can change the value of an actual argument.

Note that in both examples, the name of a dummy argument may be the same as or different from the name of the actual argument.

7.3.2 Internal procedures

Program units (the main program, external procedures and modules) may contain internal procedures. They are gathered together at the end of a program unit after the CONTAINS statement. A unit `hosts' any procedures that are contained within it. Internal procedures may not themselves contain other internal procedures and thus cannot include the CONTAINS statement.

Internal procedures may only be referenced by their host and other procedures internal to the same host, although internal procedures may invoke other (external and module) procedures.

For example:

PROGRAM outer

REAL :: a, b, c

...

CALL inner( a )

...

CONTAINS

SUBROUTINE inner( a ) !only available to outer

REAL :: a !passed by argument

REAL :: b=1.0 !redefined

c = a + b !c host association

END SUBROUTINE inner

END PROGRAM outer

The program outer contains the internal subroutine inner. Note that variables defined in the host unit remain defined in the internal procedure, unless explicitly redefined there. In the example, although a, b and c are all defined in outer:

In order to prevent redefining a variable by mistake, it is good practice to declare all variables used in a procedure.

7.3.3 External procedures

External procedures are self contained program units (subroutines or functions) that may contain (i.e. host) internal procedures. For example:

PROGRAM first

REAL :: x

x = second()

...

END PROGRAM first

FUNCTION second() !external

REAL :: second

... !no host association

END FUNCTION second

External procedures have no host program unit, and cannot therefore share data through host association. Passing data by argument is the most common way of sharing data with an external procedure. External procedures may be referenced by all other types of procedure.

7.4 Procedure variables

Any variables declared in a procedure (what ever its type) are referred to as local to that procedure, i.e. generally they cannot be used outside of the procedure in which they are declared. Dummy variables are always local to a procedure.

Variables declared inside a procedure usually only exist while the procedure in question is executing:

This `creation' and `destruction' of procedures variables means that by default, no variable declared inside a procedure retains is value from one call to the next. This default can be overcome to allow local variables to retain their values from call to call.

7.4.1 SAVE

The SAVE attribute forces the program to retain the value of a procedure variable from one call to the next. Any variable that is given an initial value in its declaration statement has the SAVE attribute by default. For example:

FUNCTION func1( a_new )

REAL :: func1

REAL :: a_new

REAL, SAVE :: a_old !saved

INTEGER :: counter=0 !saved

...

a_old = a_new

counter = counter+1

END FUNCTION func1

The first time the function func1 is referenced, a_old has an undefined value while counter is set to zero. These values are reset by the function and saved so that in any subsequent calls a_old has the value of the previous argument and counter is the number of times func1 has previously been referenced.

Note: it is not possible to save dummy arguments or function results!

7.5 Interface blocks

Interfaces occur where ever one program unit references another. To work properly a program must ensure that the actual arguments in a reference to a procedure are consistent with the dummy arguments expected by that procedure. Interfaces are checked by the compiler during the compilation phase of a program and may be:

Where ever possible interfaces should be made explicit. This can be done through the interface block:

INTERFACE

interface statements

END INTERFACE

The interface block for a procedure is included at the start of the referencing program unit. The interface statements consist of a copy of the SUBROUTINE (or FUNCTION) statement, all declaration statements for dummy arguments and the END SUNROUTINE (or FUNCTION) statement. For example:

PROGRAM count

INTERFACE

SUBROUTINE ties(score, nties)

REAL :: score(50)

INTEGER :: nties

END SUBROUTINE ties

END INTERFACE

REAL, DIMENSION(50):: data

...

CALL ties(data, n)

...

END PROGRAM count

SUBROUTINE ties(score, nties)

REAL :: score(50)

INTEGER :: nties

...

END SUBROUTINE ties

The interface block in the program count provides an explicit interface to the subroutine ties. If the count were to reference other external procedures, their interface statements could be placed in the same interface block.

7.6 Procedures arguments

7.6.1 Assumed shape objects

One of the most powerful aspects of using a procedure to perform a task is that once written and tested the procedure may be used and reused as required (even in other programs).

Since it is often the case that a program may wish to pass different sized arrays or character strings to the same procedure, Fortran 90 allows dummy arguments to have a variable sizes. Such objects are call assumed shape objects. For example:

SUBROUTINE sub2(data1, data3, str)

REAL, DIMENSION(:) :: data1

INTEGER, DIMENSION(:,:,:) :: data3

CHARACTER(len=*) :: str

...

The dummy arguments data1 and data3 are both arrays which have been declared with a rank but no size, the colon `:' is used instead of a specific size in each dimension. Similarly str has no explicit length, it adopts the length of the actual argument string. When the subroutine sub2 is called, all three dummy arguments assume the size of their corresponding actual arguments; all three dummy arguments are assumed shape objects.

7.6.2 The INTENT attribute

It is possible, and good programming practice, to specify how a dummy argument will be used in a procedure using the INTENT attribute:

For example:

SUBROUTINE invert(a, inverse, count)

REAL, INTENT(IN) :: a

REAL, INTENT(OUT) :: inverse

INTEGER, INTENT(INOUT) :: count

inverse = 1/a

count = count+1

END SUBROUTINE invert

The subroutine invert has three dummy arguments. a is used in the procedure but is not updated by it and therefore has INTENT(IN). inverse is calculated in the subroutine and so has INTENT(OUT). count (the number of times the subroutine has been called) is incremented by the procedure and so requires the INTENT(INOUT) attribute.

7.6.3 Keyword arguments

Instead of associating actual argument with dummy arguments by position only, it is possible to associate with a dummy argument by name. This can help avoid confusion when referencing a procedure and is often used when calling some of Fortran 90's intrinsic procedures. For example:

SUBROUTINE sub2(a, b, stat)

INTEGER, INTENT(IN) :: a, b

INTEGER, INTENT(INOUT):: stat

...

END SOBROUTINE sub2

could be referenced using the statements:

INTEGER :: x=0

...

CALL sub2( a=1, b=2, stat=x )

CALL sub2( 1, stat=x, b=2)

CALL sub2( 1, 2, stat=x )

The dummy variable names act as keywords in the call statement. Using keywords, the order of arguments in a call statement can be altered, however keywords must come after all arguments associated by position:

CALL sub2( 1, b=2, 0 ) !illegal

CALL sub2( 1, stat=x, 2) !illegal

When using keyword arguments the interface between referencing program unit and procedure must be explicit. Note also that arguments with the INOUT attribute must be assigned a variable and not just a value, stat=0 would be illegal.

7.6.4 Optional arguments

Occasionally, not all arguments are required every time a procedure is used. Therefore some arguments may be specified as optional, using the OPTIONAL attribute:

SUBROUTINE sub1(a, b, c, d)

INTEGER, INTENT(INOUT):: a, b

REAL, INTENT(IN), OPTIONAL :: c, d

...

END SUBROUTINE sub1

Here a and b are always required when calling sub1. The arguments c and d are optional and so sub1 may be referenced by:

CALL sub1( a, b )

CALL sub1( a, b, c, d )

CALL sub1( a, b, c )

Note that the order in which arguments appear is important (unless keyword arguments are used) so that it is not possible to call sub1 with argument d but no argument c. For example:

CALL sub1( a, b, d ) !illegal

Optional arguments must come after all arguments associated by position in a referencing statement and require an explicit interface.

It is possible to test whether or not an optional argument is present when a procedure is referenced using the logical intrinsic function PRESENT. For example:

REAL :: inverse_c

IF( PRESENT(c) ) THEN

inverse_c = 0.0

ELSE

inverse_c = 1/c

ENDIF

If the optional argument is present then PRESENT returns a value .TRUE. In the above example this is used to prevent a run-time error (dividing by zero will cause a program to `crash').

7.6.5 Procedures as arguments

It is possible to use a procedure as an actual argument in a call another procedure. Frequently it is the result of a function which is used as an actual argument to another procedure. For example:

PROGRAM test

INTERFACE

REAL FUNCTION func( x )

REAL, INTENT(IN) ::x

END FUNCTION func

END INTERFACE

...

CALL sub1( a, b, func(2) )

...

END PROGRAM test

REAL FUNCTION func( x ) !external

REAL, INTENT(IN) :: x

func = 1/x

END FUNCTION func

When the call to sub1 is made the three arguments will be a, b and the result of func, in this case the return value is 1/2. The procedure that is used as an argument will always execute before the procedure in whose referencing statement it appears begins. Using a procedure as an argument requires an explicit interface.

Note that the specification statement for the function func identifies the result as being of type REAL, this is an alternative to declaring the function name as a variable, i.e.

REAL FUNCTION func( x )

REAL, INTENT(IN) :: x

func = 1/x

END FUNCTION func

and

FUNCTION func( x )

REAL :: func

REAL, INTENT(IN) :: x

func = 1/x

END FUNCTION func

are equivalent.

7.7 Recursion

It is possible for a procedure to reference itself. Such procedures are called recursive procedures and must be defined as such using the RECURSIVE attribute. Also for functions the function name is not available for use as a variable, so a RESULT clause must be used to specify the name of the variable holding the function result, for example:

RECURSIVE FUNCTION factorial( n ) RESULT(res)

INTEGER, INTENT(IN) :: n

INTEGER :: res

IF( n==1 ) THEN

res = 1

ELSE

res = n*factorial( n-1 )

END IF

END FUNCTION factorial

Recursion may be one of two types:

both of which require the RECURSIVE attribute for the procedure A.

Recursive procedures require careful handling. It is important to ensure that the procedure does not invoke itself continually. For example, the recursive procedure factorial above uses an IF construct to either call itself (again) or return a fixed result. Therefore there is a limit to the number of times the procedure will be invoked.

7.8 Generic procedures

It is often the case that the task performed by a procedure on one data type can be applied equally to other data types. For example the procedure needed to sort an array of real numbers into ascending order is almost identical to that required to sort an array of integers. The difference between the two arrays is likely to be the data type of the dummy arguments.

For convenience, Fortran 90 allows two or more procedures to be referenced by the same, generic name. Exactly which procedure is invoked will depend on the data type (or rank) of the actual argument(s) in the referencing statement. This is illustrated by some of the intrinsic functions, for example:

The SQRT() intrinsic function (returns the square root of its argument) can be given a real, double precision or complex number as an argument:

A generic interface is required in order to declared a common name and to identify which procedures can be referred to by the name. For example:

INTERFACE swap

SUBROUTINE iswap( a, b )

INTEGER, INTENT(INOUT) :: a, b

END SUBROUTINE iswap

SUBROUTINE rswap( a, b )

REAL, INTENT(INOUT) :: a, b

END SUBROUTINE rswap

END INTERFACE

The interface specifies two subroutines iswap and rswap which can be called using the generic name swap. If the arguments to swap are both real numbers then rswap is invoked, if the arguments are both integers iswap is invoked.

While a generic interface can group together any procedures performing any task(s) it is good programming practice to only group together procedures that perform the same operation on a different arguments.

7.9 Modules

Modules are a type of program unit new to the Fortran standard. They are designed to hold definitions, data and procedures which are to be made available to other program units. A program may use any number of modules, with the restriction that each must be named separately.

The general form of a module follows that of other program units:

MODULE name

[definitions]

...

[CONTAINS

module procedures]

END [MODULE [name]]

In order to make use of any definitions, data or procedures found in a module, a program unit must contain the statement:

USE name

at its start.

7.9.1 Global data

So far variables declared in one program unit have not been available outside of that unit (recall that host association only allows procedures within the same program unit to `share' variables).

Using modules it is possible to place declarations for all global variables within a module and then USE that module. For example:

MODULE global

REAL, DIMENSION(100) :: a, b, c

INTEGER :: list(100)

LOGICAL :: test

END MODULE global

All variables in the module global can be accessed by a program unit through the statement:

USE global

The USE statement must appear at the start of a program unit, immediately after the PROGRAM or other program unit statement. Any number of modules may be used by a program unit, and modules may even use other modules. However modules cannot USE themselves either directly (module A uses A) or indirectly (module A uses module B which uses module A).

It is possible to limit the variables a program unit may access. This can act as a `safety feature', ensuring a program unit does not accidentally change the value of a variable in a module. To limit the variables a program unit may reference requires the ONLY qualifier, for example:

USE global, ONLY: a, c

This ensures that a program unit can only reference the variables a and c from the module global. It is good programming practice to USE ... ONLY those variables which a program unit requires.

A potential problem with using global variables are name clashes, i.e. the same name being used for different variables in different parts of the program. The USE statement can overcome this by allowing a global variable to be referenced by a local name, for example:

USE global, state=>test

Here the variable state is the local name for the variable test. The => symbol associates a different name with the global variable.

7.9.2 Module procedures

Just as variables declared in a module are global, so procedures contained within a module become global, i.e. can be referenced from any program unit with the appropriate USE statement. Procedures contained within a module are called module procedures.

Module procedures have the same form as external procedures, that is they may contain internal procedures. However unlike external procedures there is no need to provide an interface in the referencing program unit for module procedures, the interface to module procedures is implicit.

Module procedures are invoked as normal (i.e. through the CALL statement or function reference) but only by those program units that have the appropriate USE statement. A module procedure may call other module procedures within the same module or in other modules (through a USE statement). A module procedure also has access to the variables declared in a module through `host association'. Note that just as with other program units, variables declared within a module procedure are local to that procedure and cannot be directly referenced elsewhere.

One of the main uses for a module is to group together data and any associated procedures. This is particularly useful when derived data types and associated procedures are involved. For example:

MODULE cartesian

TYPE point

REAL :: x, y

END TYPE point

CONTAINS

SUBROUTINE swap( p1, p2 )

TYPE(point), INTENT(INOUT):: p1

TYPE(point), INTENT(INOUT):: p2

TYPE(point) :: tmp

tmp = p1

p1 = p2

p2 = tmp

END SUBROUTINE swap

END MODULE cartesian

The module carteasian contains a declaration for a data type called point. cartesian also contains a module subroutine which swaps the values of its point data type arguments. Any other program unit could declare variables of type point and use the subroutine swap via the USE statement, for example:

PROGRAM graph

USE cartesian

TYPE(point) :: first, last

...

CALL swap( first, last)

...

END PROGRAM graph

7.9.3 PUBLIC and PRIVATE

By default all entities in a module are accessible to program units with the correct USE statement. However sometimes it may be desirable to restrict access to the variables, declaration statements or procedures in a module. This is done using a combination of PUBLIC and/or PRIVATE statements (or attributes).

The PRIVATE statement/attribute prevents access to module entities from any program unit, PUBLIC is the opposite. Both may and be used in a number of ways:

MODULE one

PRIVATE !set the default for module

REAL, PUBLIC :: a

REAL :: b

PUBLIC :: init_a

CONTAINS

SUBROUTINE init_a() !public

...

SUBROUTINE init_b() !private

...

END MODULE one

7.9.4 Generic procedures

It is possible to reference module procedures through a generic name. If this is the case then a generic interface must be supplied. The form of the interface block is as follows:

INTERFACE generic_name

MODULE PROCEDURE name_list

END INTERFACE

where name_list are the procedures to be referenced via generic_name, for example a module containing generic subroutines to swap the values of two arrays including arrays of derived data types would look like:

MODULE cartesian

TYPE point

REAL :: x, y

END TYPE point

INTERFACE swap

MODULE PROCEDURE pointswap, iswap, rswap

END INTERFACE

CONTAINS

SUBROUTINE pointswap( a, b )

TYPE(point) :: a, b

...

END SUBROUTINE pointswap

!subroutines iswap and rswap

END MODULE cartesian

7.10 Overloading operators

Referencing one of several procedures through a generic interface is known as overloading; it is the generic name that is overloaded. Exactly which procedure is invoked depends on the arguments passed in the invoking statement. In a similar way to the overloading of procedure names, the existing operators (+, -, *, etc.) may be overloaded. This is usually done to define the effects of certain operators on derived data types.

Operator overloading is best defined in a module and requires an interface block of the form:

INTERFACE OPERATOR( operator )

interface_code

END INTERFACE

where operator is the operator to be overloaded and the interface_code is a function with one or two INTENT(IN) arguments. For example:

MODULE strings

INTERFACE OPERATOR ( / )

MODULE PROCEDURE num

END INTERFACE

CONTAINS

INTEGER FUNCTION num( s, c )

CHARACTER(len=*), INTENT(IN) :: s

CHARACTER, INTENT(IN) :: c

num = 0

DO i=1,LEN( s )

IF( s(i:i)==c ) num=num+1

END DO

END FUNCTION num

END MODULE strings

Usually, the / operator is not defined for characters or strings but the module strings contains an interface and defining function to allow a string to be divide by a character. The result of the operation is the number of times the character appears in the string:

USE strings

...

i = `hello world'/'l' !i=3

i = `hello world'/'o' !i=2

i = `hello world'/'z' !i=0

7.11 Defining operators

As well as overloading existing operators, it is possible to define new operators. This is particularly useful when manipulating derived data types. Any new operator(s) have the form.name. and their effect is defined by a function. Just as with overloaded operators, the defining function requires an INTERFACE OPERATOR block and one or two non-optional INTENT(IN) arguments, for example:

MODULE cartesian

TYPE point

REAL :: x, y

END TYPE point

INTEFACE OPERATOR ( .DIST. )

MODULE PROCEDURE dist

END INTERFACE

CONTAINS

REAL FUNCTION dist( a, b )

TYPE(point) INTENT(IN) :: a, b

dist = SQRT( (a%x-b%x)**2 + (a%y-b%y)**2 )

END FUNCTION dist

END MODULE cartesian

The operator .DIST. is used to find the distance between two points. The operator is only defined for the data type point, using it on any other data type is illegal. Just as with overloaded operators, the interface and defining function are held in a module. It makes sense to keep the derived data type and associated operator(s) together.

Any program unit may make use of the data type point and the operator .DIST. by using the module cartesian, for example:

USE cartesian

TYPE(point) :: a, b

REAL :: distance

...

distance = a .DIST. b

7.12 Assignment overloading

It is possible to overload the meaning of the assignment operator (=) for derived data types. This again requires an interface, this time to a defining subroutine. The subroutine must have two, non-optional arguments, the first must have INTENT(INOUT) or INTENT(OUT); the second must have INTENT(IN). For example:

MODULE cartesian

TYPE point

REAL :: x, y

END TYPE point

INTEFACE ASSIGNMENT( = )

MODULE PROCEDURE max_point

END INTERFACE

CONTAINS

SUBROUTINE max_point( a, pt )

REAL, INTENT(OUT) :: a

TYPE(point), INTENT(IN) :: pt

a = MAX( pt%x, pt%y )

END SUBROUTINE max_point

END MODULE cartesian

Using the module cartesian allows a program unit to assign a type point to a type real. The real variable will have the largest value of the components of the point variable. For example:

USE cartesian

TYPE(point) :: a = point(1.7, 4.2)

REAL :: coord

...

coord = a !coord = 4.2

7.13 Scope

7.13.1 Scoping units

The scope of a named entity (variable or procedure) is that part of a program within which the name or label is unique. A scoping unit is one of the following:

All variables, data types, labels, procedure names, etc. within the same scoping unit must have a different names. Entities with the same name, which are in different scoping units, are always separate from one another.

7.13.2 Labels and names

All programs and procedures have their own labels (e.g. see FORMAT statements later). Therefore it is possible for the same label to appear in different program units or internal procedures without ambiguity. The scope of a label is the main program or a procedure, excluding any internal procedures.

The scope of a name (for say a variable) declared in a program unit is valid from the start of the unit through to the unit's END statement. The scope of a name declared in the main program or in an external procedure extends to all internal procedures unless redefined by the internal procedure. The scope of a name declared in an internal procedure is only the internal procedure itself - not other internal procedures.

The scope of a name declared in a module extends to all program units that use that module, except where an internal procedure re-declares the name.

The names of program units are global and must therefore be unique. The name of a program unit must also be different from all entities local to that unit. The name of an internal procedure extends throughout the containing program unit. Therefore all internal procedures within the same program unit must have different names.

The following shows an example of scoping units:

MODULE scope1 !scope 1

... !scope 1

CONTAINS !scope 1

SUBROUTINE scope2() !scope 2

TYPE scope3 !scope 3

... !scope 3

END TYPE scope3 !scope 3

INTERFACE !scope 3

... !scope 4

END INTERFACE !scope 3

REAL :: a, b !scope 3

10 ... !scope 3

CONTAINS !scope 2

FUNCTION scope5() !scope 5

REAL :: b !scope 5

b = a+1 !scope 5

10 ... !scope 5

END FUNCTION !scope 5

END SUBROUTINE !scope 2

END MODULE !scope 1

7.14 Exercises

  1. Write a program with a single function to convert temperatures from Fahrenheit to Centigrade. In the body of the main program read in the temperature to be converted, and output the result. The actual calculation is to be done in a function.

    a) Write an internal function which requires no actual arguments, but which uses host association to access the value to be converted. The result of the function is the converted temperature.

    b) Write an external function which requires the temperature to be converted to be passed as a single argument. Again the function result is the converted temperature. Do not forget to include an interface block in the main program.

    Use the following formula to convert from Fahrenheit to Centigrade:

  2. Write a program with a single subroutine to sort a list of integer numbers into order. In the main program read a list of random integers (about 5) into an array, call the subroutine to perform the sort, and output the array.

    a) Write an internal subroutine which requires no actual arguments, but which uses host association to access the array to be sorted.

    b) Write an external subroutine which requires that the array to be sorted be passed as an argument. The external subroutine will require an interface block.

    Use the following selection sort algorithm to sort the values in an array a:

    INTEGER :: a(5), tmp

    INTEGER :: j, last, swap_index(1)

    last = SIZE( a )

    DO j=1, last-1

    swap_index = MINLOC( a(j:last) )

    tmp = a( j )

    a( j ) = a( (j-1)+swap_index(1) )

    a( (j-1)+swap_index(1) ) = tmp

    END DO

    The selection sort algorithm passes once through the array to be sorted, stopping at each element in turn. At each element the remainder of the array is checked to find the element with the minimum value, this is then swapped with the current array element.

  3. Write a program which declares three rank one, real arrays each with 5 elements and that uses array constructors to set a random value for each element (say between 1 and 20) for each array. Write an internal subroutine which finds the maximum value in an array (use the MAX and MAXVAL intrinsic function) and reports and SAVEs that value. Call the subroutine once for each array, the final call should report the maximum value from all arrays.

  4. Change the subroutine in written in 3 to accept arrays of any size (if you have not already done so). Test the new subroutine by calling it with three arrays, each of different size.

  5. Write a program which declares an rank 1, integer array and use a constructor to set values for each element in the range -10 to 10. The program will pass the array as an argument to an external subroutine, along with two optional arguments top and tail.

    The subroutine is to replace any values in the array greater than top with the value of top; similarly the subroutine replaces any values lower than tail with tail. The values of top and tail are read in by the main program. If either top or tail is absent on call then no respective action using the value is taken. (Remember it is good programming practice to refer to all optional arguments by keyword.)

  6. Write a module to contain the definition for a derived data type point, which consists of two real numbers representing the x an y coordinates of that point. Along with this declaration, include a global parameter representing the origin at (0.0,0.0).

    The module should also contain a function to calculate the distance between two arbitrary points (this is done earlier in the notes, as an operator). Write a program to read in an x and y coordinate and calculate its distance from the origin.

  7. Using the selection sort algorithm in question 2 write a module containing two subroutines, one which sorts real arrays the other which sorts integer arrays (both of rank one). The module should provide a generic interface to both subroutines. Check the module and the generic interface by writing a program that uses the module.


[Next] [Previous] [Top]
All documents are the responsibility of, and copyright, © their authors and do not represent the views of The Parallel Computer Centre, nor of The Queen's University of Belfast.
Maintained by Alan Rea, email A.Rea@qub.ac.uk
Generated with CERN WebMaker