# Function and subroutine

February 2020

Back to index.html.

# Function and subroutine

## Program units

In the previous chapters, the Fortran program was in a single unit defined in the program statement. It is called main program; in the previous chapter, any program you wrote had the main program. When the main program gets longer, it is a good idea to break the program down into small, manageable pieces. Such a small group of code is called program unit.

A typical program-unit is a function. We have looked at several intrinsic functions. In Fortran, you can define a custom function as a block of programs. A function accepts arguments (input values), and it always returns a single value.

Another program-unit is subroutine, which is more flexible than functions. A subroutine can accept any number of values as the argument, and it can return any number of values (even no return values). Functions and subroutines are similar, but the only difference is whether it returns a single value or not. In the later sections in this tutorial, we refer to functions and subroutines as procedures.

## External function and subroutines

We first look at how external procedures (functions or subroutines) are defined. An external procedure is an independent program unit. The main program does not know what the external is, so the programmer should tell the main program what they are.

### External function

#### Definition

As seen in the previous chapters, a function has a form like sqrt(x) with a name and arguments, and it returns a value. A custom function is defined with the name, what kind of arguments it accepts, and what it returns. The function statement defines a new function. Here is a template of the definition.

function name(arguments) result(output)
List of arguments
Definition of output
...
end function name

For example, the following function returns the cubic root of the argument. It works because $v=\sqrt[3]{x}=x^{\frac{1}{3}}$.

function cubicroot(x) result(v)
implicit none
real,intent(in) :: x
real :: v
v = x**(1.0/3.0)
end function cubicroot

The usage of custom function is the same as the intrinsic functions. It can take variables or literals as arguments.

real :: my_value,output

! case 1
my_value = 1.5
output = cubicroot(my_value)

! case 2
print *,cubicroot(2.0)

! case 3
my_value = cubicroot(my_value)/1.25

You can see how a function is defined.

• A function is defined between function and end function.
• The input values are passed through the argument variables.
• The function statement defines its name, the argument variables in (), and the return variable in result().
• You should put implicit none inside the function.
• At the top of the function, all arguments should be declared as variables with the status like intent(in).
• Also, the return variable should be declared. The value held by this variable will be returned when the function finishes.

When defining the function, you know the type of input, but you do not know the actual value of the input. So, you should declare the input as an argument variable with an arbitrary name, and use it in the function. The argument variable is a “reference” (or alias) to the original value, and accessing the argument variable is equivalent to access to the original variable or literals. Because the argument variable is just a label to the original data, some people call it dummy variable or dummy argument. In the above example, you can think x refers to the original variable (or literal).

The function statement defines an independent program-unit from the main program. As you do in program, you have to put implicit none in the function. All the variables declared inside the function are unrelated to the main program.

The intent() attribute declares the status of the input variable to clarify which input can be changed (or unchanged).

• intent(in): It will be used as an input variable, and it will not be altered in the function.
• intent(out): It will not be used as an input variable, but it will be altered in the function.
• intent(inout): It may be used as an input variable, and also, it may be altered in the function.

If the argument does not have any above attributes, it will be treated as intent(inout). Note that if the argument has intent(out), you must put a value to this argument. Otherwise, the program may return a nonsense value.

Technically, you can define a function that returns multiple values with argument variables with intent(out). However, in modern Fortran, there is a recommendation that the function does not change the input arguments, and it just return a value. Changing the input arguments, you should use subroutine which is intended for such purposes.

#### Alternative definition

One may like an alternative declaration of a function.

real function cubicroot(x)
implicit none
real,intent(in) :: x
cubicroot = x**(1.0/3.0)
end function cubicroot

In this way, the return type is specified in front of the function name. The name of the return variable is the same as the function name, and you do not need the result keyword and the declaration of the variable. I am not going to use this style in the rest of the tutorial.

#### Program structure

When using this custom function in the main program, you can put the function in the same source file, for the simplest usage. Then, using the interface statement, you tell the main program what the custom function you are going to use is.

program custom
implicit none
real :: a,b
interface
function cubicroot(x) result(v)
implicit none
real,intent(in) :: x
real :: v
end function cubicroot
end interface

! use a custom function as a regular function
b = cubicroot(a)
print *,b
end program custom

function cubicroot(x) result(v)
implicit none
real,intent(in) :: x
real :: v
v = x**(1.0/3.0)
end function cubicroot

The interface statement includes the definition of a custom function, including the name, the argument variables, and the return value. It is precisely the same as the first a couple of lines in the function statement. If you use several external procedures, you can put all of their definitions in the interface statement.

You may think it is awkward that you have to repeat the definition of a function in interface in the main program. It is frustrating, but it is required because the main program does not know what cubicroot is. The external function is unrelated to the main program, which does not know the type of arguments and its return value. If the main program fails to use the custom function, the program may crash or result in wrong behavior. This redundancy in interface can be removed by using a module that we introduce in the next chapter.

#### Scope of variable

Some readers should wonder what happens if the main program and the external function both have the same variable names. Are these variables share the values between two program units? Or, the same variable name does not mean to share the actual value? Another question is the accessibility to the variables. Can a program unit refer to a variable defined in a separate program unit? All the answers is “no”.

The external function is independent of the main program. The function should be self-descriptive, and all the variables in this function are unrelated to ones used outside the function. A variable is accessible only in the program unit where the variable is declared. Scope is the region of a program unit where the variable is valid.

When a variable is accessive from a single program-unit, we say it is a local (or private) variable. All the variables declared in an external function are local. The scope of any variables defined in the function is limited to the inside of the function. See the following example to see the scope of variables.

program custom
implicit none
real :: a,b
...

! This a is unrelated to the function's a.
b = cubicroot(a)
print *,b
end program custom

function cubicroot(x) result(v)
implicit none
real,intent(in) :: x
real :: v,a
! This a is a local variable exclusively used in this function.
a = (1.0/3.0)
v = x**a
end function cubicroot

In this example, the variable a is defined both in the main program and the function, but two are unrelated. Using the scope rule, you don’t have to worry about the conflict of the names among program units.

### External subroutine

#### Definition

The definition of an external subroutine is the same as function, but using a different statement subroutine.

subroutine name(arguments)
List of arguments
...
end subroutine name

First of all, you do not need a return value for the subroutine. You do not have to return any values. If you want to return some values, you have to alter the arguments. You can change multiple arguments with intent(out) and intent(inout).

As an example, the following subroutine returns two solutions of a quadratic equation, $ax^2+bx+c=0$.

subroutine solutions_quadratic_equations(a,b,c,s1,s2)
implicit none
real,intent(in) :: a,b,c
real,intent(out) :: s1,s2
real :: d
d = b**2 - 4*a*c
if(d>=0) then
s1 = (-b + sqrt(d))/(2*a)
s2 = (-b - sqrt(d))/(2*a)
else
print *,'b**2 - 4*a*c < 0'
stop
end if
end subroutine solutions_quadratic_equations

In this subroutine, the first 3 arguments (a,b, and c) should be for input, and the last two (s1 and s2) are for output. The solutions are not defined as real numbers if $d=b^2-4ac<0$, and in this case, the program stops.

#### Program structure

Same as functions, you have to put the definition of custom subroutines in interface. To call a subroutine, use the call statement with required arguments.

program custom
implicit none
real :: a,b,c,v1,v2
interface
implicit none
real,intent(in) :: a,b,c
real,intent(out) :: s1,s2
end interface

! use a custom function as a regular function
print *,v1,v2
end program custom

implicit none
real,intent(in) :: a,b,c
real,intent(out) :: s1,s2
real :: d
d = b**2 - 4*a*c
if(d>=0) then
s1 = (-b + sqrt(d))/(2*a)
s2 = (-b - sqrt(d))/(2*a)
else
print *,'b**2 - 4*a*c < 0'
stop
end if
end subroutine solutions_quadratic_equations

The scope of variables is the same as as functions. In the above case, the variables a, b, and c are exclusively used in each program unit.

### Summary

• An external procedure (function or subroutine) is independent of the main program.
• Variables are accessible in the program unit where they are defined.
• A function is defined with the function statement and a subroutine with subroutine.
• In a definition of procedure, all the arguments should be declared as variables with intent.
• A function should return a single value, but a subroutine does not have to return values or can return any number of values.

### Exercises

1. Write an external function to have 3 real numbers: $x$ and $\mu$, and $\sigma^2$, and return the standardized value $z=(x-\mu)/\sigma$.
2. Write an external subroutine to accept one real number and print the logarithm of it.

## Internal function and subroutines

If a function or a subroutine is tightly associated with another program unit, you can put the procedure inside the program unit. The included function or subroutine is called internal procedure.

One most significant advantage of the internal procedure is that it can access all the variables defined in the parent program-unit. In other words, the main program and the internal procedures share the same scope of variables. The internal procedures inherit all the variables and configurations (like implicit none) defined in the main program.

### Definition of internal procedure

#### basic form

An internal procedure is included in the main program. The definition of the internal procedure has the same form as an external one. The following is an internal version of solution-of-quadratic-equation subroutine, shown above.

program custom
implicit none
real :: a,b,c,v1,v2

print *,v1,v2

contains
real,intent(in) :: a,b,c
real,intent(out) :: s1,s2
real :: d
d = b**2 - 4*a*c
if(d>=0) then
s1 = (-b + sqrt(d))/(2*a)
s2 = (-b - sqrt(d))/(2*a)
else
print *,'b**2 - 4*a*c < 0'
stop
end if
end program custom

You will see several rules in this program to use internal procedures.

• The internal procedure should be placed between contains and end program. Multiple internal-procedures can be described.
• The contains keyword should be at the end of the body of the main program.
• Once the main program uses implicit none, this is also effective in all the internal procedures; each internal procedure does not have to have implicit none.
• The main program does not need the interface statement to use internal procedures.

In the above program, the internal subroutine still keeps its independence. All required variables come from the arguments, and two output variables will be returned through the arguments. There is 1 local variable in the internal subroutine, and this is accessible only in the subroutine (the main program cannot touch it).

#### Shared variables

When you try to share the variables between the main and the internal procedure, the program looks like this.

program custom
implicit none
real :: a,b,c,v1,v2

print *,v1,v2

contains
real :: d
d = b**2 - 4*a*c
if(d>=0) then
s1 = (-b + sqrt(d))/(2*a)
s2 = (-b - sqrt(d))/(2*a)
else
print *,'b**2 - 4*a*c < 0'
stop
end if
end program custom

In this case, the variables a, b, c, v1, and v2, which are not declared in the internal subroutine, will be recognized as shared variables with the main program. The internal subroutine does not have to use arguments to access the values; it can just read and write the variables in the main program. To call this subroutine, the user should still use () with the subroutine name. It is useful when the main program exclusively requires internal procedures.

### Internal procedures in external procedures

Internal procedures can reside in any program unit. For example, an external function can include some internal procedures. It is useful if the internal procedure is a part of the program unit. The following example shows an external subroutine with an internal function.

program custom
implicit none
real :: a,b,c
interface
subroutine print_status(a,b,c)
implicit none
real,intent(in) :: a,b,c
ens subroutine print_status
end interface

call print_status(a)
end program custom

subroutine print_status(a,b,c)
implicit none
real,intent(in) :: a,b,c

if(discriminant(a,b,c)>=0) then
print *,'The equation has real solutions.'
else
print *,'The equation has imaginary solutions.'
end if

contains
function discriminant(a,b,c) result(d)
real,intent(in) :: a,b,c
real :: d
d = b**2 - 4*a*c
end function discriminant
end subroutine print_status

Technically, the internal function discriminant can access all the variables defined in print_status (but in the above case, the variables are passed as the argument).

### Which should be used, external or internal?

There are pros and cons to external and internal procedures. There are a few recommendations about which should be used in your case.

The external procedures are preferred if your procedure will be re-used in other places. The procedure should be independent and self-described in terms of the variable scope. The independent procedure is more comfortable to test and modify. The disadvantage is that you have to declare the interface of the procedure before using it.

The internal procedure is a good option if it is exclusively used by the program unit that includes the procedure. The procedure shares the same scope of variables as the parent program-unit. And therefore, the internal procedure is not portable when it depends on outside variables. Also, when you have many internal procedures, it becomes unclear which and when internal procedures change the variables defined in the parent unit. You need a custom rule for which variables should be shared between the procedures.

Because of the usability, in this tutorial, I am going to use external procedures mainly. The disadvantage of a redundant interface will be removed by using modules, which is introduced in the next chapter. Also, I use an internal procedure if it is the right way to do a job.

### Summary

• A program unit can include internal procedures.
• All the variables and the implicit none status in the parent unit will be shared with the internal procedures. The internal procedure can still have its local variables.
• The internal procedures stay after the contains statement in the parent unit. Any number of internal procedures can be included.
• The internal procedures are exclusively used only by the parent program unit so that no interface is needed.
• External procedures are useful if they will be re-used in different programs. Internal procedures are useful if it is closely related to the variables defined in the parent unit.

### Exercises

1. Rewrite the program of cubicroot using an internal function.

## Various argument types

You can pass any objects, such as scalar, arrays, and derived-type variables, to functions and subroutines through arguments. In this section, I am going to use internal procedures just for simplicity, although each procedure is re-usable.

### Arrays

To use an array as an argument, the user should specify its dimension. The following function accepts a real vector and returns the average of elements.

program custom
implicit none
real :: obs(5)

print *,'mean=',mean(obs)

contains
! assumed-shape array
function mean(x) return (m)
real,intent(in) :: x(:)
real :: m
integer :: n
n = size(x)
m = sum(x)/n
end function mean
end program custom

The argument is an assumed-shape array which declares only the dimension of an array with (:), and its actual size is separately available using some functions like size. If a matrix (2-dimensional array) is passed, the declaration is like x(:,:). You need a local, integer variable (here n) to get the size of the input array.

There is another style to pass an array to procedures.

program custom
implicit none
real :: obs(5)

print *,'mean=',mean(obs)

contains
! fixed-shape array
function mean(n,x) return (m)
integer,intent(in) :: n
real,intent(in) :: x(n)
real :: m
m = sum(x)/n
end function mean
end program custom

This style is called fixed-shape. In this method, the size of array x is fixed, and the size is given another argument n. The programmer is responsible for making sure that the actual array size equals to n; if it is not true, the result of this function is unknown. In general, the fixed-share array is not recommended unless you have a definite reason to use it.

### Characters

A string can be an argument, and the length is not necessarily specified.

program custom
implicit none
character(len=10) :: string

print *,'Is the fiest letter alphabet? ',first_alphabet(string)

contains

function first_alphabet(s) return (yesno)
character(len=*),intent(in) :: s
logical :: yesno
integer :: p
p = scan(s(1:1),"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
if(p==1) then
yesno = .true.
else
yesno = .false.
end if
end function first_alphabet
end program custom

The length of the argument string can be len=*, and the actual length is available by a function len (or len_trim). In the above example, the length is not obtained because it is not needed.

### Derived types

A derived type is also supported as an argument.

program custom
implicit none
type animal
character(len=10) :: name
integer :: age
end type animal
type(animal) :: a

a%name = "beta-gamma"
a%age = 1
call print_info(a)

contains

subroutine print_info(x)
type(animal),intent(in) :: x
print *,"name=",x%name
print *,"age =",x%age
end subroutine print_info
end program custom

The input/output status intent(in) affects all member variables of x. In this case, the members are read-only.

### Exercises

1. Write a subroutine to print a matrix with a structure.
2. Write a function to indicate whether the first two letters are alphabet or not.

## Quick return

You may want to exit in the middle of a procedure quickly. The return statement performs the quick return to the caller. To see the effect, we look back to the “alphabet” function.

function first_alphabet(s) return (yesno)
character(len=*),intent(in) :: s
logical :: yesno
integer :: p

! error check
if(len(s)==0) then
yesno = .true.
return
end if

! main body
p = scan(s(1:1),"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
if(p==1) then
yesno = .true.
else
yesno = .false.
end if
end function first_alphabet

It checks the length of s, and if the length is 0 (i.e. "" is given), the function is terminated to return .false.. When the program meets return, the remaining code will not be performed, and the focus immediately returns to the original caller.

Regarding the above program, there are many ways to result in the same logic, even without return.

function first_alphabet(s) return (yesno)
character(len=*),intent(in) :: s
logical :: yesno
integer :: p

! error check
if(len(s)==0) then
yesno = .true.
else
! main body
p = scan(s(1:1),"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
if(p==1) then
yesno = .true.
else
yesno = .false.
end if
end if
end function first_alphabet

It is related more to a style of programming. Many people empirically know what a better style is, but it is not a topic in this tutorial. See some books dealing with programming style.

### Exercises

There is no exercise in this subsection.

Back to index.html.