组织代码结构#
大多数编程语言允许你将常用代码收集到 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
它们的程序单元之前编译。
Optional arguments#
An advantage of placing subroutines and functions in modules is that they can have optional
arguments. In a procedure with an argument declared optional, the present
function is used to test if the argument was set in the caller. Optional arguments that are not present may not be accessed within the procedure. Here is a generalization of the vector_norm
function that can use powers other than 2 to compute the Lp norm.
module norm_mod
implicit none
contains
function vector_norm(vec,p) result(norm)
real, intent(in) :: vec(:)
integer, intent(in), optional :: p ! power
real :: norm
if (present(p)) then ! compute Lp norm
norm = sum(abs(vec)**p) ** (1.0/p)
else ! compute L2 norm
norm = sqrt(sum(vec**2))
end if
end function vector_norm
end module norm_mod
program run_fcn
use norm_mod
implicit none
real :: v(9)
v(:) = 9
print *, 'Vector norm = ', vector_norm(v), vector_norm(v,2)
print *, 'L1 norm = ', vector_norm(v,1)
end program run_fcn