函数
目录
子程序
子程序 (SUBROUTINE):写程序时,可以把某一段常被使用、具有特定功能的程序代码封装成子程序。后续只需要通过该子程序的 CALL 命令,就能使用这段代码的功能,如:
program array
implicit none
! 通过 call 调用相应子程序
call message("first")
call message("second")
end program array
! 声明子程序时,同时包括参数的声明,尽管之后还需要进一步声明参数类型
subroutine message(hint)
implicit none
! intent 关键字指定参数的输入输出类型
! * in 输入
! * out 输出
! * inout 输入输出(默认,不指明 intent 时)
character(len=*), intent(in) :: hint
! ! 设置为字符串参数并具有输入类型时,应该用 character(len=*) 声明
! ! 反之会以固定长度读取缓存区的内容并输出,可能会出现错误
write(*,*) "get message: ", hint
end subroutine message
程序输出为:
get message: first
get message: second
子程序类似于 C/C++ 中的 void 函数,无返回值
- intent(in):表示参数是输入参数。这意味着在子程序中,参数只能被读取,不能被修改。它通常用于传递数据给子程序进行计算,但不需要在子程序中修改参数的值。
- intent(out):表示参数是输出参数。这意味着在子程序中,参数的初始值可能不重要,但在子程序执行完毕后,参数的值将被修改并传递给调用程序。在子程序中,可以对输出参数进行赋值或修改。
- intent(inout):表示参数既是输入参数又是输出参数。这意味着在子程序中,参数的初始值对计算结果有影响,并且在子程序执行完毕后,参数的值将被修改并传递给调用程序。在子程序中,可以读取和修改输入/输出参数的值。
subroutine 错误样例
以下错误无法通过编译
program array
implicit none
call message("first")
call message("second")
end program array
subroutine message(hint)
implicit none
character(len=*), intent(in) :: hint
write(6,*) "get message: ", hint
hint = "finish"
! ^ 此处编译出错
! ^ 无法修改 intent(in) 参数的值
end subroutine message
以下错误均能通过编译但运行失败
! 运行错误原因:数组越界访问
program array
implicit none
call message("first")
call message("second")
end program array
subroutine message(hint)
implicit none
character(len=*), intent(inout) :: hint
write(6,*) "get message: ", hint
hint = "end"
end subroutine message
subroutine 小结
- 参数传递通过传递地址实现,不传值
- 子程序参数中的字符串应用 character(len=*) 声明
- 应指明子程序参数中数组的维度和各维的起始和终止编号
- 可以将数组的部分内容传递给子程序,此时编译器会分配存储部分数组内容的临时空间
- 可以用 intent(in)、intent(out)、intent(inout) 来指定参数的输入输出属性,未指定时,按照 inout 方式对待
- 直接调用子程序时,编译器并不检查调用者和子程序之间参数的一致性,可能出现数组大小不一致、数组维度信息不一致、变量类型不一致、参数个数与顺序不一致等会导致运行过程或结果有误的情况
自定义函数
自定义函数 (FUNCTION):与子程序类似(包括传参的功能特点),但是函数有返回值,而子程序没有返回值,如:
program array
implicit none
integer, external :: sum
integer :: i
i = sum(1, 2)
write(*,*) i
end program array
integer function sum(a, b)
implicit none
integer, intent(in) :: a, b
! 返回值
sum = a + b
end function sum
函数类似于 C/C++ 中的有返回值的函数
当调用者中声明的数据类型与自定义函数返回值类型不同时,编译器不报错,但可能出现奇怪的错误运行结果:
program array
implicit none
integer, external :: sum ! 此处声明的是整型
integer :: i
real :: r
i = sum(1, 2)
r = sum(1,2)
write(*,*) i
end program array
real function sum(a, b) ! 此处定义的为浮点数
implicit none
real, intent(in) :: a, b
sum = a + b
end function sum
上述函数在运行过程中,会因为编译器的种类和版本乃至优化参数的不同,出现不同的运行结果。因此,在调用函数时,应该保证调用者中声明的数据类型与自定义函数返回值类型一致。
传递函数
在传递参数时,除了传递数字、字符等基本类型的变量外,还可以把一个函数名当做参数传递出去,如:
program array
integer :: i
real, external :: plus2
real, external :: sin
read(*,*) i
if (i == 1) then
call message(plus2)
else
call message(sin)
end if
end program array
subroutine message(func)
implicit none
real, external :: func(1.0)
write(*,*) func(1.0)
end subroutine message
real function plus2(a)
implicit none
real, intent(in) :: a
plus2 = a + 2.0
end function plus2
函数的接口使用
函数接口 (INTERFACE):在 Fortran 中,函数的接口是指函数的声明部分,即函数名、参数个数、参数类型、参数顺序等信息。函数的接口信息是在调用函数时,编译器用来检查调用者和被调用者之间参数的一致性的重要依据。下列情况中需要使用 INTERFACE:
- 函数返回值为数组时
- 指定参数位置来传递参数时
- 函数有可选参数时
- 函数返回值为指针时
函数接口相当于函数重载(但不完全等同),其实现起来相当费力,本质是相同逻辑的重复与内存换运行的策略,具体逻辑为
调用函数接口 -> 函数接口索引到指定函数 -> 调用函数
。
subroutine add_int(a, b)
implicit none
integer, intent(in) :: a, b
write(*,*) a + b
end subroutine add_int
subroutine add_real(a, b)
implicit none
real, intent(in) :: a, b
write(*,*) a + b
end subroutine add_real
program main
interface add
subroutine add_int(a, b)
implicit none
integer, intent(in) :: a, b
end subroutine add_int
subroutine add_real(a, b)
implicit none
real, intent(in) :: a, b
end subroutine add_real
end interface add
call add(1, 2)
! ^ 调用 add_int
call add(1.0, 2.0)
! ^ 调用 add_real
end program main
可选参数
INTERFACE 中的子程序或自定义函数可以具有可选参数(用 optional 声明),在调用 INTERFACE 时,可以不提供可选参数的值,如:
program main
interface server
subroutine send_value(hint, r, i)
implicit none
character(len=*), intent(in) :: hint
real, intent(in), optional :: r
integer, intent(in), optional :: i
end subroutine send_value
end interface server
call server("first", r = 0.1)
call server("second", i = 1)
call server("third", r , v)
end program main
subroutine send_value(hint, r, i)
implicit none
character(len=*) hint
real, optional :: r
integer, optional :: i
! use present to check if the optional parameter is provided
if (present(r)) write(6,*) hint, r
if (present(i)) write(6,*) hint, i
end subroutine send_value
在子程序或自定义函数中使用可选参数时,应先用 present 确认是否提供了可选参数的值
特殊的函数类型
递归函数
递归函数即在运行时需要调用自身,调用自身时,需要使用 recursive
关键字,下面给出一个计算阶乘的递归函数,subroutine 同理
program main
implicit none
integer :: i
i = factorial(5)
write(*,*) i
end program main
recursive function factorial(n) result(r)
implicit none
integer, intent(in) :: n
integer :: r
if (n == 0) then
r = 1
else
r = n * factorial(n - 1)
end if
end function factorial
内部函数
内部函数 (INTERNAL FUNCTION):在 Fortran 中,可以在一个(子)程序或自定义函数内声明一个函数/子程序,这个函数/子程序只能在该(子)程序或自定义函数内使用,使用 contain
关键字,下面给出以主程序为例的内部函数实例,子程序和函数同理
program main
implicit none
integer :: i
i = factorial(5)
write(*,*) i
contains
recursive function factorial(n) result(r)
implicit none
integer, intent(in) :: n
integer :: r
if (n == 0) then
r = 1
else
r = n * factorial(n - 1)
end if
end function factorial
end program main
program other
implicit none
integer :: i
i = factorial(5)
! ^ 此处编译出错,无法调用到 factorial
write(*,*) i
end program other
PURE 函数
PURE 函数:函数的返回值仅与输入参数有关,不与程序中其他变量有关,使用 pure
关键字,下面给出一个计算阶乘的 PURE 函数,subroutine 同理
program main
real :: x = 2.0, y = 3.0 , sum
sum = add(x, y)
print *, "The sum of", x, "and", y, "is", sum
end program main
pure function add(a, b) result(result)
real, intent(in) :: a, b
real :: result
result = a + b
end function add
ELEMENTAL 函数
ELEMENTAL 函数:函数的输入参数和返回值都是数组,且数组的每个元素都是独立计算的,使用 elemental
关键字,需要注意的是,声明函数参数时,应以元素出发,而不是数组,尽管在调用前后都是以数组的形式返回,下面给出一个计算阶乘的 ELEMENTAL 函数
program main
implicit none
integer, dimension(5) :: a
interface
! * 此处使用 interface 来描述函数接口
elemental function plus_one(n) result(r)
implicit none
integer, intent(in) :: n
integer :: r
end function plus_one
end interface
a = plus_one([1, 2, 3, 4, 5])
write(*,*) a
end program main
elemental function plus_one(n) result(r)
implicit none
integer, intent(in) :: n
integer :: r
r = n + 1
end function plus_one
MODULE
// todo
模块 (MODULE):在 Fortran 中,模块是一种特殊的程序单元,它可以包含变量、子程序、自定义函数等。模块的主要作用是将程序中的变量、子程序、自定义函数等按照功能进行分类和封装,便于程序的编写和维护。模块的使用方法如下:
module myModule
implicit none
real :: r = 1.0
integer :: i = 1
end module myModule
program main
use myModule
implicit none
call message()
end program main
subroutine message()
use myModule
implicit none
write(*,*) r, i
end subroutine message
module 中声明的变量默认为全局变量
module 小结
- 可以在一个 module 中声明该 module 内不同子程序、自定义函数或函数共享使用的公共变量,公共变量可以声明为仅在 module 内访问(声明为 private),或可被 module 外开放访问(默认情况或声明为 public);类似的,一个 module 的子程序、自定义函数或函数也可以声明为 private
- 可使用一个module所有开放访问的变量和子程序、自定义函数或函数,也可以指定所使用的具体变量和子程序、自定义函数或函数
- 在使用 module 内的子程序、自定义函数或函数时,会对参数列表进行检查
- 可以通过重命名所使用 module 的变量名、子程序、自定义函数或函数,以使用不同 module 的同名资源