Functions and Subroutines in Fortran

Fortran, one of the earliest high-level programming languages, was specifically designed for numerical and scientific computation. To write modular, maintainable, and reusable code, Fortran provides functions and subroutines. These two constructs allow programmers to organize programs into logical blocks that perform specific tasks. Understanding the differences between functions and subroutines and how to implement them is fundamental to effective Fortran programming.

This post provides an in-depth discussion on functions, subroutines, their differences, and practical examples of their usage.

1. Introduction to Functions and Subroutines

In Fortran, functions and subroutines are both procedures, but they serve slightly different purposes:

  • Function: A function is a procedure that returns a single value. It is typically used when a computation needs to produce a value that can be assigned to a variable or used in expressions.
  • Subroutine: A subroutine is a procedure that performs a specific task but does not return a value directly. Subroutines can modify variables via arguments and are often used for tasks like printing, input/output, or performing operations on arrays.

Breaking a program into functions and subroutines makes it easier to read, debug, and reuse code.


2. Difference Between Functions and Subroutines

FeatureFunctionSubroutine
Return ValueReturns a single valueDoes not return a value directly
Usage in ExpressionsCan be used in expressionsCannot be used in expressions
Declarationfunction function_name(arguments)subroutine subroutine_name(arguments)
InvocationCalled by using its name in expressionsCalled using the call statement
Example UseComputing square, cube, trigonometric valuesPrinting a message, modifying arrays, performing repeated tasks

3. Creating and Using Functions

Functions in Fortran are designed to perform calculations and return a value. The syntax for a function is:

function function_name(arguments)
data_type :: function_name, argument1, argument2, ...
! Body of the function
function_name = expression
end function function_name

Key Points:

  1. The function name must match the return variable.
  2. Arguments can be optional, but all must have declared types.
  3. Functions return exactly one value.

3.1 Example: Square Function

The following example defines a function that calculates the square of a number.

program test_square
real :: result
result = square(3.0)
print *, "Square of 3 =", result
contains
function square(x)
    real :: square, x
    square = x * x
end function square
end program test_square

Explanation:

  • The square function takes one real argument x.
  • It computes the square of x and returns the result.
  • The contains block is used in modern Fortran to define internal functions.

3.2 Function with Multiple Arguments

Functions can also accept multiple arguments.

program test_average
real :: result
result = average(10.0, 20.0, 30.0)
print *, "Average =", result
contains
function average(a, b, c)
    real :: average, a, b, c
    average = (a + b + c) / 3.0
end function average
end program test_average

Explanation:

  • The average function takes three real numbers as input.
  • It calculates and returns their average.
  • Functions make computations reusable in different parts of the program.

3.3 Function Returning Integer Values

Functions can also return integer values:

program test_factorial
integer :: result
result = factorial(5)
print *, "Factorial of 5 =", result
contains
function factorial(n)
    integer :: factorial, n, i
    factorial = 1
    do i = 1, n
        factorial = factorial * i
    end do
end function factorial
end program test_factorial

Explanation:

  • The factorial function calculates the factorial of an integer n.
  • The return value is of type integer.
  • The loop multiplies all numbers from 1 to n to compute the factorial.

4. Creating and Using Subroutines

Subroutines in Fortran are designed to perform tasks that may not return a value directly but can modify variables passed to them. Subroutines are invoked using the call statement.

Syntax:

subroutine subroutine_name(arguments)
data_type :: argument1, argument2, ...
! Body of the subroutine
end subroutine subroutine_name

Key Points:

  1. Arguments passed to subroutines can be modified within the subroutine.
  2. Subroutines are called using the call keyword.
  3. Subroutines are often used for input/output operations, data processing, or repetitive tasks.

4.1 Example: Greeting Subroutine

program test_greet
call greet("Alice")
call greet("Bob")
contains
subroutine greet(name)
    character(len=20) :: name
    print *, "Hello,", name
end subroutine greet
end program test_greet

Explanation:

  • The greet subroutine prints a greeting message.
  • It accepts a character argument name and prints “Hello, [name]”.
  • The subroutine is invoked using call greet("Alice").

4.2 Subroutine Modifying Variables

Subroutines can change the values of variables passed as arguments:

program test_square_sub
real :: num, result
num = 4.0
call compute_square(num, result)
print *, "Square of", num, "=", result
contains
subroutine compute_square(x, y)
    real :: x, y
    y = x * x
end subroutine compute_square
end program test_square_sub

Explanation:

  • compute_square is a subroutine that calculates the square of x and stores it in y.
  • Unlike functions, subroutines do not return a value but can modify multiple variables.

4.3 Subroutine with Multiple Arguments

program test_statistics
real :: a, b, mean, diff
a = 10.0
b = 20.0
call calculate_stats(a, b, mean, diff)
print *, "Mean =", mean
print *, "Difference =", diff
contains
subroutine calculate_stats(x, y, avg, delta)
    real :: x, y, avg, delta
    avg = (x + y) / 2.0
    delta = x - y
end subroutine calculate_stats
end program test_statistics

Explanation:

  • The calculate_stats subroutine takes two numbers as input.
  • It calculates the mean and difference and assigns them to avg and delta.

5. Internal vs External Procedures

5.1 Internal Procedures

Internal procedures are defined within a program or module using the contains statement. Examples above, like square and greet, are internal procedures.

Advantages:

  • Access to program variables.
  • Encapsulation and readability.

5.2 External Procedures

External procedures are defined outside the main program or module and linked during compilation.

Example:

! file: myfunctions.f90
function cube(x)
real :: cube, x
cube = x**3
end function cube
program test_cube
real :: result
result = cube(3.0)
print *, "Cube of 3 =", result
end program test_cube

Compilation Command:

gfortran myfunctions.f90 test_cube.f90 -o test_cube

Explanation:

  • The function cube is external to the main program.
  • It is compiled along with the main program to produce the executable.

6. Modules for Functions and Subroutines

Modules allow grouping functions and subroutines together for reusability.

Example:

module math_operations
contains

function square(x)
    real :: square, x
    square = x * x
end function square
subroutine greet(name)
    character(len=20) :: name
    print *, "Hello,", name
end subroutine greet
end module math_operations program test_module
use math_operations
real :: result
result = square(5.0)
print *, "Square of 5 =", result
call greet("Charlie")
end program test_module

Explanation:

  • The math_operations module contains both a function and a subroutine.
  • The main program uses the module to access its procedures.

7. Best Practices for Functions and Subroutines

  1. Use descriptive names that indicate their purpose.
  2. Keep functions focused on computing a single value.
  3. Use subroutines for tasks that involve multiple variables or I/O operations.
  4. Use implicit none in procedures to avoid undeclared variables.
  5. Document the arguments and return values clearly.
  6. Prefer modules for grouping related procedures for better code organization.

8. Summary

  • Functions return a single value and can be used in expressions.
  • Subroutines do not return values directly but can modify arguments.
  • Functions and subroutines enhance code reusability, readability, and modularity.
  • Modern Fortran encourages the use of modules and internal procedures.
  • Proper use of arguments, return values, and meaningful names leads to maintainable programs.

9. Complete Example: Using Both Functions and Subroutines

module utilities
contains

function square(x)
    real :: square, x
    square = x * x
end function square
subroutine greet(name)
    character(len=20) :: name
    print *, "Hello,", name
end subroutine greet
function factorial(n)
    integer :: factorial, n, i
    factorial = 1
    do i = 1, n
        factorial = factorial * i
    end do
end function factorial
end module utilities program main_program
use utilities
real :: num
integer :: n
character(len=20) :: person
num = 6.0
print *, "Square of", num, "=", square(num)
n = 5
print *, "Factorial of", n, "=", factorial(n)
person = "Alice"
call greet(person)
end program main_program

Explanation:

  • The module utilities contains a function square, a subroutine greet, and a function factorial.
  • The main program uses the module and demonstrates the usage of functions and subroutines.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *