1.5. 函数与子程序

Fortran 中有两种过程的组织形式:函数 (function) 和 子程序 (subroutine)。

1.5.1. 函数(Function)

1.5.1.1. 基本语法

! 函数定义
function add(a, b) result(sum)
    implicit none
    integer, intent(in) :: a, b
    integer :: sum           ! 返回值

    sum = a + b
end function add

! 调用
integer :: x = 3, y = 4, z
z = add(x, y)                ! z = 7

1.5.1.2. 简写形式

! 省略 result 子句(函数名作为返回值变量)
integer function square(x)
    implicit none
    integer, intent(in) :: x
    square = x * x
end function square

小技巧

推荐总是使用 result 子句,这样函数名和返回值变量名分离更清晰。

1.5.2. intent 属性

属性

含义

效果

intent(in)

输入参数

过程内不可修改

intent(out)

输出参数

进入过程时值未定义

intent(inout)

输入输出参数

可读可修改

subroutine calc(a, b, c)
    implicit none
    real, intent(in)    :: a      ! 只读
    real, intent(out)   :: b      ! 只写
    real, intent(inout) :: c      ! 可读可写

    b = a * 2.0
    c = c + a
end subroutine

重要

在可能的情况下**始终使用 intent** (标注 intent 属性): - 帮助编译器检查错误 - 生成更优化的代码 - 提高代码自文档性

1.5.3. 子程序(Subroutine)

子程序不返回值(但可以通过参数返回)。

subroutine swap(a, b)
    implicit none
    integer, intent(inout) :: a, b
    integer :: temp

    temp = a
    a = b
    b = temp
end subroutine swap

! 调用
integer :: x = 3, y = 5
call swap(x, y)             ! x=5, y=3

1.5.4. 函数 vs 子程序

特性

函数 (function)

子程序 (subroutine)

返回值

以一个返回值为主

无返回值,通过参数输出

调用方式

result = func(args)

call sub(args)

用法

表达式内使用

作为独立语句

适合场景

数学计算、判断

修改参数、I/O 操作

1.5.5. 递归过程

! 递归函数
recursive function factorial(n) result(res)
    implicit none
    integer, intent(in) :: n
    integer :: res

    if (n <= 1) then
        res = 1
    else
        res = n * factorial(n - 1)
    end if
end function factorial

! 递归子程序
recursive subroutine hanoi(n, from, to, aux)
    implicit none
    integer, intent(in) :: n
    character(1), intent(in) :: from, to, aux

    if (n == 1) then
        write(*, '(A, A, A, A)') "移动盘 1 从 ", from, " 到 ", to
    else
        call hanoi(n - 1, from, aux, to)
        write(*, '(A, I0, A, A, A, A)') "移动盘 ", n, " 从 ", from, " 到 ", to
        call hanoi(n - 1, aux, to, from)
    end if
end subroutine hanoi

小心

递归函数必须使用 recursive 关键字! Fortran 95 之前的版本不支持递归。

1.5.6. pure 与 elemental 过程

1.5.6.1. pure 过程

不产生任何副作用的函数,可安全并行执行:

pure function square(x) result(res)
    implicit none
    real, intent(in) :: x
    real :: res
    res = x * x
end function

1.5.6.2. elemental 过程

对标量定义,但可自动逐元素作用于数组:

elemental function celsius_to_fahrenheit(c) result(f)
    implicit none
    real, intent(in) :: c
    real :: f
    f = c * 9.0 / 5.0 + 32.0
end function

! 直接传入数组
real :: temps(5) = [0.0, 10.0, 20.0, 30.0, 40.0]
write(*, *) celsius_to_fahrenheit(temps)
! 输出: 32.0 50.0 68.0 86.0 104.0

1.5.7. 可选参数与关键字参数

subroutine write_log(msg, level, unit)
    implicit none
    character(*), intent(in)           :: msg
    integer, intent(in), optional      :: level
    integer, intent(in), optional      :: unit

    integer :: lvl, u

    lvl = 0
    if (present(level)) lvl = level
    u = 6   ! 标准输出
    if (present(unit)) u = unit

    write(u, '(A, I2)') trim(msg), lvl
end subroutine

! 调用(可选参数可省略)
call write_log("信息")                  ! 全部使用默认值
call write_log("警告", level=1)         ! 指定关键参数
call write_log("错误", level=2, unit=0) ! 指定多个

小技巧

使用 present() 检查可选参数是否被传入。 关键字参数使调用更清晰,避免记错参数顺序。

1.5.8. interface 关键字

用于声明外部过程或操作符的重载:

interface
    subroutine external_sub(x, y)
        import :: dp          ! 导入主调模块的 dp 类型
        real(dp), intent(in)  :: x
        real(dp), intent(out) :: y
    end subroutine
end interface

! 操作符重载
interface operator(+)
    procedure add_vectors
end interface