组织代码结构#
大多数编程语言允许你将常用代码收集到 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
它们的程序单元之前编译。