Fortran编程语法整理
- 01 Fortran 中的程序单元
- 02 子程序与函数
- 03 stop命令后跟说明
- 04 do concurrent
- 05 纯过程(pure procedure)
- 06 Fortran中的elemental 属性
- 07 Fortran函数和子程序中的可选参数
- 08 Fortran内置模块iso_fortran_env
- 09 在Fortran中自定义模块
- 10 Fortran模块中的私有与公有声明
- 11 在导入模块时重命名导入的实体
- 12 在纯过程中数组不能在声明时直接初始化
- 13 可分配数组的一些用法
- 14 Fortran中的空数组
- 15 可分配数组相关用法
- 16 索引和切片
- 17 Fortran内置函数 all
- 18 Fortran内置函数pack
- 参考文献
01 Fortran 中的程序单元
- main program (主程序) —— 只能从操作系统调用的最高级别程序单元;
- function (函数) —— 从表达式调用的可执行子函数,并且总是返回单个结果;
- subroutine (子程序)—— 一种可执行的子程序,可以就地修改多个参数,但不能在表达式中使用;
- module(模块)—— 变量、函数和子例程定义的不可执行集合;
- submodule(子模块)—— 扩展现有模块,用于定义只有该模块可以访问的变量和过程定义,对于更复杂的应用程序和库非常有用
1-1 submodule(子模块)
在Fortran中,子模块(submodule)是模块系统的一部分,用于帮助组织和管理大型代码库。子模块允许将模块分成多个部分,这些部分可以独立开发和维护,从而提高代码的可读性和可维护性。
下面是子模块的一些关键点和示例:
关键点:
- 主模块:定义公共接口、类型和公共变量。
- 子模块:实现主模块中定义的接口和包含实际的代码逻辑。
- 结构:子模块依赖于主模块,子模块中的任何子程序都必须在主模块中声明。
- 可见性:子模块可以访问主模块中的所有公共实体。
示例:
# 主模块(Module)
module my_module
implicit none
! 在这里声明子模块将实现的接口
contains
procedure :: my_subroutine
end module my_module
# 子模块(Submodule)
submodule (my_module) my_submodule
implicit none
contains
subroutine my_subroutine
! 子程序实现
print *, "Hello from submodule!"
end subroutine my_subroutine
end submodule my_submodule
# 使用子模块
program main
use my_module
implicit none
call my_subroutine
end program main
详细解释:
-
主模块 (Module)
module my_module
定义了一个名为my_module
的主模块。- 在
contains
部分,声明了一个子程序my_subroutine
,但没有提供实现。这告诉编译器子程序将在子模块中定义。
-
子模块 (Submodule)
submodule (my_module) my_submodule
声明了一个名为my_submodule
的子模块,并指定它从属于my_module
。- 在
contains
部分,实现了my_subroutine
子程序。
-
主程序 (Main Program)
use my_module
使主模块my_module
中的所有公共实体在主程序中可用。call my_subroutine
调用子模块中的my_subroutine
子程序。
这种结构允许更好的代码组织和模块化,使得大型Fortran项目更容易管理和维护。
02 子程序与函数
2-1 概述
![[Pasted image 20240522135037.png|500]]
- Fortran中函数可以接收任意数量的输入参数,但是只能返回一个结果。如果有多个结果需要返回可以使用数组或自定义数据类型。
- 对于没有副作用的计算,应该使用函数来完成。
在Fortran以及其他编程语言中,副作用(side effect)指的是在函数执行过程中对其外部状态(如全局变量、输入参数、I/O操作等)的修改。没有副作用的函数(即纯函数)只依赖于其输入参数,并且除了返回值外不会对程序的其他部分产生影响。这种函数的输出仅由输入决定,不会改变任何外部状态。
在Fortran中,当提到“在没有副作用时使用函数”时,通常指的是:
- 函数仅依赖于输入参数:函数的计算结果完全由传递给它的参数决定,没有依赖或修改外部变量。
- 没有修改输入参数:函数不会通过传递的参数修改外部变量的值。
- 没有I/O操作:函数不进行输入输出操作(如读取文件或打印输出,打印到屏幕上也是一种副作用)。
2-2 函数与子程序特性
(1)为函数结果指定不同名称
可以使用 result
属性为函数结果指定一个不同的名称,而不是直接使用函数名称:
INTEGER FUNCTION SUM(A, B)
INTEGER, INTENT(IN) :: A, B
SUM = A + B
END FUNCTION SUM
! 指定不同的函数返回名称
INTEGER FUNCTION SUM(A, B) RESULT(RES)
INTEGER, INTENT(IN) :: A, B
RES = A + B
END FUNCTION SUM
一个函数应该只做一件事。
函数适合于不会产生副作用的计算,而子例程则更适合于需要就地修改变量的情况。但要注意这只是最佳实践,而不是硬性规定,Fortran语法是允许函数中就地修改变量的。
03 stop命令后跟说明
if(grid_size <= 0) stop 'grid_size must be > 0'
04 do concurrent
在Fortran中,concurrent
是用在并行编程中的关键字,通常在 DO CONCURRENT
结构中使用。DO CONCURRENT
结构允许程序员明确表示循环迭代之间可以独立并行执行,从而提高计算效率。
4-1 do concurrent
的使用
基本示例:
program concurrent_example
implicit none
integer :: i
integer, dimension(10) :: array
! Initialize the array
array = 0
! DO CONCURRENT loop
do concurrent (i = 1:10)
array(i) = i * i
end do
! Print the array
print *, array
end program concurrent_example
在这个示例中,DO CONCURRENT
循环用于对数组 array
的每个元素进行平方计算。由于每次迭代之间没有数据依赖关系,编译器可以将这些迭代并行化,以提高执行效率。
带条件的并行循环
program concurrent_with_conditions
implicit none
integer :: i
integer, dimension(10) :: array
! Initialize the array
array = 0
! DO CONCURRENT loop with conditions
do concurrent (i = 1:10)
if (mod(i, 2) == 0) then
array(i) = i * 2
else
array(i) = i * 3
end if
end do
! Print the array
print *, array
end program concurrent_with_conditions
这个示例展示了带条件语句的 DO CONCURRENT
循环。根据 i
是否为偶数,数组 array
的元素被赋予不同的值。
注意事项:
- 独立性:
DO CONCURRENT
结构的每次迭代必须是独立的,也就是说,每次迭代之间不应有数据依赖性。 - 副作用:避免在
DO CONCURRENT
结构中使用具有副作用的操作(如 I/O 操作),因为这可能导致不可预测的行为。 - 性能:尽管
DO CONCURRENT
可以显式指示并行性,实际的性能提升仍然依赖于编译器的实现和底层硬件支持。
高级示例:
program concurrent_advanced
implicit none
integer :: i, j
integer, dimension(5, 5) :: matrix
! Initialize the matrix
matrix = 0
! DO CONCURRENT nested loop
do concurrent (i = 1:5, j = 1:5)
matrix(i, j) = i * j
end do
! Print the matrix
do i = 1, 5
print *, matrix(i, :)
end do
end program concurrent_advanced
这个高级示例展示了嵌套的 DO CONCURRENT
循环,用于初始化一个 5x5 的矩阵。每个元素 matrix(i, j)
的值为 i * j
。
通过使用 DO CONCURRENT
,Fortran 程序可以更好地利用多核处理器的并行计算能力,从而提高计算性能。
05 纯过程(pure procedure)
在Fortran中,纯过程(pure procedure)是一类没有副作用的过程。这意味着纯过程不能更改外部变量、不能执行 I/O 操作,也不能调用任何非纯过程。纯过程的主要优点是它们更容易进行并行化和优化,因为编译器可以更好地预测其行为。
纯过程可以是子程序(subroutine)或函数(function)。要声明一个过程为纯过程,可以使用 pure
关键字。
\
纯过程的规则:
- 不能改变通过参数传递的任何变量的值。
- 不能更改模块、公共或局部变量的值。
- 不能执行输入/输出(I/O)操作。
- 不能调用非纯过程。
示例:
纯函数(Pure Function)
module math_module
implicit none
contains
pure function add(a, b) result(c)
integer, intent(in) :: a, b
integer :: c
c = a + b
end function add
end module math_module
在这个示例中,add
是一个纯函数,它接受两个整数参数 a
和 b
,并返回它们的和。因为它不修改任何外部状态,也不执行 I/O 操作,所以它是纯的。
纯子程序(Pure Subroutine)
module math_module
implicit none
contains
pure subroutine swap(a, b)
integer, intent(inout) :: a, b
integer :: temp
temp = a
a = b
b = temp
end subroutine swap
end module math_module
在这个示例中,swap
是一个纯子程序,它交换两个整数参数 a
和 b
的值。尽管它修改了参数的值,但它不修改任何外部状态或执行 I/O 操作,因此它仍然是纯的。
使用纯过程
program main
use math_module
implicit none
integer :: x, y, z
x = 3
y = 4
z = add(x, y) ! 调用纯函数
print *, "Sum:", z
call swap(x, y) ! 调用纯子程序
print *, "Swapped values:", x, y
end program main
在这个示例中,我们定义了一个主程序 main
,它使用 math_module
中的纯过程。它首先调用纯函数 add
来计算两个整数的和,然后调用纯子程序 swap
来交换两个整数的值。
纯过程的优点:
- 可并行化:因为纯过程没有副作用,编译器可以更容易地并行化这些过程。
- 优化:纯过程更容易被编译器优化,因为它们的行为是确定的。
- 可测试性:纯过程由于没有副作用,更容易进行单元测试。
通过使用纯过程,Fortran程序可以提高代码的可靠性、可维护性和性能。
06 Fortran中的elemental 属性
在Fortran中,elemental
属性用于声明可以对数组的每个元素进行操作的过程(函数或子程序)。带有 elemental
属性的过程可以对标量和数组参数进行统一处理,而无需显式编写循环。这使得代码更简洁,并且可以利用编译器的自动向量化和并行化优化。
6-1 elemental
关键字的使用:
elemental
函数:
module math_module
implicit none
contains
elemental function square(x) result(y)
real, intent(in) :: x
real :: y
y = x * x
end function square
end module math_module
在这个示例中,square
函数使用了 elemental
关键字,可以对标量或数组的每个元素进行平方计算。
elemental
子程序:
module math_module
implicit none
contains
elemental subroutine add_one(x)
real, intent(inout) :: x
x = x + 1.0
end subroutine add_one
end module math_module
在这个示例中,add_one
子程序使用了 elemental
关键字,可以对标量或数组的每个元素加一。
使用 elemental
子程序:
program main
use math_module
implicit none
real :: a, b
real, dimension(5) :: array, result_array
! 标量操作
a = 3.0
b = square(a)
print *, "Square of a:", b
! 数组操作
array = [1.0, 2.0, 3.0, 4.0, 5.0]
result_array = square(array)
print *, "Square of array:", result_array
! 对数组应用elemental子程序
call add_one(array)
print *, "Array after add_one:", array
end program main
在这个示例中:
- 对标量
a<