组织代码结构#

大多数编程语言允许你将常用代码收集到 procedures 中,这些代码可以通过从其它代码部分 calling 来重用。

Fortran 有两种形式的过程:

  • 子例程:由 call 语句调用

  • 函数:在返回值的表达式或赋值中调用

子程序和函数都可以通过 argument association 访问父范围内的变量;除非指定了 value 属性,否则这类似于按引用调用。

子程序#

子程序输入参数,称为 dummy arguments,在子程序名称后面的括号中指定;虚拟参数类型和属性在子例程的主体中声明,就像局部变量一样。

例子:

! Print matrix A to screen
subroutine print_matrix(n,m,A)
  implicit none
  integer, intent(in) :: n
  integer, intent(in) :: m
  real, intent(in) :: A(n, m)

  integer :: i

  do i = 1, n
    print *, A(i, 1:m)
  end do

end subroutine print_matrix

注意声明虚拟参数时附加的 intent 属性;这个可选属性向编译器表示参数是“只读”(intent(in))“只写”(intent(out))还是“读写”( intent(inout)) 在过程中。在这个例子中,子程序不会修改它的参数,因此所有的参数都是 intent(in)

始终为虚拟参数指定 intent 属性是一种很好的做法;这允许编译器检查意外错误并提供自文档。

我们可以使用 call 语句从程序中调用此子例程:

program call_sub
  implicit none

  real :: mat(10, 20)

  mat(:,:) = 0.0

  call print_matrix(10, 20, mat)

end program call_sub

这个例子使用了一个所谓的 explicit-shape 数组参数,因为我们传递了额外的变量来描述数组 A 的维度;如果我们将子例程放在后面描述的模块中,这将不是必需的。

函数#

! L2 Norm of a vector
function vector_norm(n,vec) result(norm)
  implicit none
  integer, intent(in) :: n
  real, intent(in) :: vec(n)
  real :: norm

  norm = sqrt(sum(vec**2))

end function vector_norm

在生产代码中,应使用内部函数 norm2

要执行此函数:

program run_fcn
  implicit none

  real :: v(9)
  real :: vector_norm

  v(:) = 9

  print *, 'Vector norm = ', vector_norm(9,v)

end program run_fcn

函数不修改其参数是一种很好的编程习惯 —— 也就是说,所有函数参数都应该是 intent(in)。这样的函数被称为 pure 函数。如果你的过程需要修改其参数,请使用子例程。

模块#

Fortran 模块包含程序、过程和其它模块可以通过 use 语句访问的定义。它们可以包含数据对象、类型定义、过程和接口。

  • 模块允许受控范围扩展,从而明确实体访问

  • 模块自动生成现代程序所需的显式接口

建议始终将函数和子例程放在模块中。

例子:

module my_mod
  implicit none

  private  ! All entities are now module-private by default
  public public_var, print_matrix  ! Explicitly export public entities

  real, parameter :: public_var = 2
  integer :: private_var

contains

  ! Print matrix A to screen
  subroutine print_matrix(A)
    real, intent(in) :: A(:,:)  ! An assumed-shape dummy argument

    integer :: i

    do i = 1, size(A,1)
      print *, A(i,:)
    end do

  end subroutine print_matrix

end module my_mod

Compare this print_matrix subroutine with that written outside of a module. We no longer have to explicitly pass the matrix dimensions and can instead take advantage of assumed-shape arguments since the module will generate the required explicit interface for us. This results in a much simpler subroutine interface.

要在程序中 use 模块:

program use_mod
  use my_mod
  implicit none

  real :: mat(10, 10)

  mat(:,:) = public_var

  call print_matrix(mat)

end program use_mod

示例: 显式导入列表

use my_mod, only: public_var

示例: 别名导入

use my_mod, only: printMat=>print_matrix

每个模块都应该写在一个单独的 .f90 源文件中。模块需要在任何 use 它们的程序单元之前编译。