Fortran/Fortran procedures and functions

From Wikibooks, open books for an open world
Jump to: navigation, search

Part of the Fortran WikiBook

Functions and Subroutines[edit]

In most programs, a block of code is often re-used at several places. In order to minimize duplicating code and facilitate maintaining the code, such blocks of code should be placed within a function or subroutine. A Fortran function is similar to a mathematical function, which takes one or many parameters as inputs and returns a single output value. A Fortran subroutine is a block of code that performs some operation on the input variables, and as a result of calling the subroutine, the input variables are modified.

An expression containing a function call:

  ! func1 is a function defined elsewhere
  ! it takes an integer as an input and returns another integer as the output
  a = func1(b)

A call to a subroutine:

  ! sub1 is a subroutine defined elsewhere
  ! sub1 performs some operation on input variables e and f
  call sub1(e, f)
  ! now e or f, or both (or neither) may be modified

Many programming languages do not distinguish between functions and subroutines (e.g. C/C++, Python, Java). Pure functional programming languages (e.g. Haskell) only allows functions, because subroutines modify input variables as side-effects, which can complicates the code in some cases. In Fortran, functions and subroutines are different: the former returns a value while the latter does not.

Functions are simpler than subroutines. A function can only return one variable, and can be invoked from within a write statement, inside an if declaration if (function) then, etc. A subroutine handles many variables and can only be used as a stand-alone command (using the keyword call).

Function[edit]

In Fortran, one can use a function to return a value or an array of values. The following program calls a function to compute the sum of the square and the cube of an integer.

 function func(i) result(j)
    integer, intent(in) :: i ! input
    integer             :: j ! output
    j = i**2 + i**3
 end function func
 
 program xfunc
    implicit none
    integer :: i
    integer :: func
    i = 3
    print*,"sum of the square and cube of",i," is",func(i)
 end program xfunc

The intent(in) attribute of argument i means that i cannot be changed inside the function. Note that the return type of func needs to be declared. If this is omitted, some compilers will not compile. Open64 will compile the resulting code with warning, but the behaviour is ill-defined.

An alternative formulation (F77 compatible) is

 FUNCTION func_name(a, b)
    INTEGER :: func_name
    INTEGER :: a
    REAL    :: b
    func_name = (2*a)+b
    RETURN
 END FUNCTION
 
 PROGRAM cows
    IMPLICIT NONE
    INTEGER :: func_name
    PRINT *,func_name(2, 1.3)
 END PROGRAM

The return type of the func_name still needs to be declared, as above. The only difference is how the return type of func_name is referenced within func_name. In this case, the return variable has the same name as the function itself.

Subroutine[edit]

A subroutine can be used to return several values through its arguments. It is invoked with a call statement. Here is an example.

subroutine square_cube(i,isquare,icube)
  integer, intent(in)  :: i             ! input
  integer, intent(out) :: isquare,icube ! output
  isquare = i**2
  icube   = i**3
end subroutine square_cube
 
program xx
  implicit none
  integer :: i,isq,icub
  i = 4
  call square_cube(i,isq,icub)
  print*,"i,i^2,i^3=",i,isq,icub
end program xx

Intent[edit]

When declaring variables inside functions and subroutines that need to be passed in or out, intent may be added to the declaration.

intent(in) means that the variable value can enter, but not be changed

intent(out) means the variable is set inside the procedure and sent back to the main program with any initial values ignored.

intent(inout) means that the variable comes in with a value and leaves with a value (default).

More on Functions vs. Subroutines[edit]

Both functions and subroutines can modify their input variables. By necessity, subroutines modify input variables, since they do not return any output value. Functions do not have to, but are allowed, by default, to modify input variables. A function can be turned into a pure function, which does not have any side-effects through the use of the intent attribute on all input variables, and further enforced through the keyword pure. (The pure keyword imposes additional restrictions, which essentially prevents the function from having any side-effects.)

An example of a pure function.

pure function square(x)
  real, intent(in) :: x
  real :: square
  square = x * x
end function
 
program main
  real :: a, b, square
  a = 2.0
  b = square(a)
  ! After invoking the square(.) pure function, we can be sure that 
  ! besides assigning the output value of square(a) to b,
  ! nothing else has been changed.
end program main