03-Fortran基础--Fortran函数和子程序
- 0 引言
- 1 各函数介绍
- 1.1 函数(Functions)
- 1.2. 子程序(Subroutines)
- 1.3 递归函数(Recursive Functions)
- 1.4. 泛型函数(Generic Functions)
- 1.5. Pure 函数
- 1.6 逐元(Element)函数
- 1.7 抽象函数
- 2 结语
0 引言
在Fortran中,函数和子程序(也称为过程)是实现重复使用代码的重要方式。这部分介绍下几类函数,并提供相应的示例。
1 各函数介绍
1.1 函数(Functions)
一般形式:
FUNCTION function_name(argument1, argument2, ...)result(result_value)
! 声明变量
TYPE,[intent(in/out/inout)] :: argument1, argument2, ...
TYPE :: result_value
TYPE :: variable1, variable2, ...
! 执行的代码
result_value = [过程中返回]
END FUNCTION function_name
调用形式
result_value = function_name(argument1, argument2, ...)
FUNCTION
关键字用于声明函数。function_name
是函数的名称。argument1, argument2, ...
是函数的参数。intent(in/out/inout)
intent声明函数参数的输入、输出属性,可以省略。 如果是intent(in)表明输入参数不允许被改;intent(out)表明参数为输出参数,是要被赋值的;intent(inout)表明输入参数可被修改,也是默认参数;variable1, variable2, ...
是在函数中使用的局部变量。result_value
是函数的返回值。
示例:
program test_fun
implicit none
real(8) :: x,y ! 函数主体中声明两个变量,x作为函数输入变量要赋初值
x = 5.d0
y = Square(x) ! 调用函数返回结果y
print *,x,"的平方=",y ! 打印函数返回结果
contains ! 关键字:包含关系,上面program过程可以调用函数square,该写法不唯一
function square(x)result(y) ! 函数部分
real(8),intent(in) :: x ! 声明函数输入变量x
real(8) :: y ! y为函数返回
y = x * x
end function Square
end program Console1
运行结果:
在这个示例中,Square 函数接受一个实数参数 x,并返回 x 的平方赋值给y。
1.2. 子程序(Subroutines)
一般形式:
SUBROUTINE subroutine_name(argument1, argument2, ...)
! 声明变量
TYPE,intent(in/out/inout) :: variable1, variable2, ...
! 执行的代码
END SUBROUTINE subroutine_name
调用形式
call subroutine_name(argument1, argument2, ...)
SUBROUTINE
关键字用于声明子程序。subroutine_name
是子程序的名称。intent(in/out/inout)
声明变量输入、输出属性。argument1, argument2, ...
是子程序的参数。variable1, variable2, ...
是在子程序中使用的局部变量。
示例:
program test_subroutine
implicit none
character(len=:),allocatable :: str ! 定义一动态字符串
str = "Li Ming" ! 给动态字符串赋值,'='对于动态字符串或动态数组有分配空间和赋值的含义;
call PrintHello(name) ! 调用子过程,打印结果
contains
SUBROUTINE PrintHello(name) ! 子过程
CHARACTER(LEN=*) :: name ! 输入参数,LEN=* 实质是传入的指针数组,获取的是传入参数的地址;
PRINT *, "Hello, ", name, "!"
END SUBROUTINE PrintHello
end program
在这个示例中,PrintHello 子程序接受一个字符串参数 name,并打印出问候语。
1.3 递归函数(Recursive Functions)
递归函数是指调用自身的函数。它们通常用于解决可以通过反复将问题分解为更小的子问题来解决的问题,递归可以对function递归,也可以对subroutine递归。
递归函数的一般形式:
! function 递归的一般形式
recursive FUNCTION recursive_function_name(argument1, argument2, ...)result(result)
! 声明变量
TYPE,intent(in/out/inout) :: argument1, argument2, ...
TYPE :: result
TYPE :: variable1, variable2, ...
! 基本情况(递归终止条件)
IF (base_case_condition) THEN
result = base_case_value
ELSE
! 递归情况
result = recursive_function_name(recursive_call_arguments)
END IF
END FUNCTION recursive_function_name
! subroutine 递归的一般形式
recursive subroutine recursive_subroutine_name(argument1, argument2, ...)
call recursive_subroutine_name(recursive_call_arguments)
end subroutine recursive_subroutine_name
recursive
关键字,声明该函数为递归函数。recursive_function_name
是递归函数的名称。argument1, argument2, ...
是递归函数的参数。variable1, variable2, ...
是在递归函数中使用的局部变量。base_case_condition
是递归函数的终止条件。base_case_value
是递归函数在终止条件下返回的值。recursive_call_arguments
是递归调用时传递给函数的参数。result
递归函数的返回值。
以阶乘的递归函数调用过程为例:
program test_recursive
implicit none
integer :: n,fibo
n = 5
fibo = fact(n) ! 调用递归函数
print *,n,"的阶乘=",fibo
contains
recursive function fact(n)result(res) ! 计算阶乘的递归函数
implicit none
integer, intent(in) :: n ! 输入参数
integer :: res ! 返回参数
if (n == 0) then
res = 1
else
res = n * fact(n-1)
end if
end function fact
end program
1.4. 泛型函数(Generic Functions)
在Fortran中,泛型函数允许你为相同的函数名称定义多个具有不同特征(参数类型或数目)的版本。这样可以根据参数的不同调用不同的函数版本。下面是泛型函数的一般形式和使用教程:
泛型函数的一般形式:
MODULE generic_module
INTERFACE generic_function
MODULE PROCEDURE :: specific_function1, specific_function2, ...
END INTERFACE generic_function
CONTAINS
! 函数1
FUNCTION specific_function1(arg1, arg2, ...)
! 具体实现
END FUNCTION specific_function1
! 函数2
FUNCTION specific_function2(arg1, arg2, ...)
! 具体实现
END FUNCTION specific_function2
! 其他具体函数的定义
END MODULE generic_module
MODULE
关键字用于定义模块。INTERFACE
关键字用于定义泛型接口。generic_function
是泛型接口的名称。specific_function1, specific_function2, ...
是具体函数的名称,每个具体函数都应该有特定的参数列表。
实例: 定义泛型模块和具体函数
MODULE MathFunctions
INTERFACE GenericFunction
MODULE PROCEDURE Add, Multiply
END INTERFACE GenericFunction
CONTAINS
FUNCTION Add(a, b)
REAL :: a, b
Add = a + b
END FUNCTION Add
FUNCTION Multiply(a, b)
REAL :: a, b
Multiply = a * b
END FUNCTION Multiply
END MODULE MathFunctions
使用泛型函数:
PROGRAM TestGenericFunction
USE MathFunctions
REAL :: x, y, result
x = 5.0
y = 3.0
result = GenericFunction(x, y)
PRINT *, "Result:", result
END PROGRAM TestGenericFunction
在这个例子中,GenericFunction 是泛型接口,它可以根据参数类型自动选择调用 Add 还是 Multiply 函数。在 result = GenericFunction(x, y) 中,根据 x 和 y 的类型,编译器会自动选择调用适当的函数。
泛型函数在处理不同数据类型或不同参数个数的情况下非常有用,它们提高了代码的可重用性和灵活性。
1.5. Pure 函数
在Fortran中,PURE 函数是指不改变任何全局状态(如模块变量、共享内存等)且只依赖于其输入参数的函数。这种函数对于给定相同输入参数时始终返回相同结果,因此具有很好的可重复性和可测试性。下面是 PURE 函数的一般形式和实例:
PURE 函数的一般形式:
PURE FUNCTION pure_function_name(arg1, arg2, ...)
! 参数声明
TYPE :: arg1, arg2, ...
! 函数体
! 返回值赋值
END FUNCTION pure_function_name
PURE
关键字用于声明函数是纯函数,即不修改全局状态。pure_function_name
是纯函数的名称。arg1, arg2, ...
是函数的参数。
函数体中不能修改任何全局状态,包括不得对模块变量进行更改或调用具有SAVE属性的子程序。
实例:
program test_pure
real(8) :: x,y
x = 3.14
y = square(x) ! 函数调用
print *,x,"的平方是",y
contains
pure function square(x)result(res) ! 纯函数定义
real(8),intent(in) :: x
real(8) :: res
res = x * x
end function square
end program
这是一个简单的纯函数,计算输入参数的平方。在这个例子中,函数 Square 不改变任何全局状态,只是根据输入参数 x 计算并返回其平方。使用纯函数的好处包括:
可重复性
:对于相同的输入参数,纯函数始终返回相同的结果。可测试性
:因为纯函数不依赖于全局状态,所以更容易进行单元测试。优化机会
:编译器可以更容易地进行优化,因为它们不必考虑全局状态的影响。- 在编写纯函数时,要确保遵循纯函数的定义,不要修改任何全局状态,并且要确保函数的输出只依赖于其输入参数。
1.6 逐元(Element)函数
Element 函数用于逐元素操作数组。这种函数允许对整个数组进行操作而不需要显式循环,在定义函数或子程序的时候添加Element 关键字即可,注意逐元函数的输入实际为数组的每一个元素,所以类型要一致。
示例:
program test_element
implicit none
integer :: i,j
real(8) :: x(3,3),y(3,3) ! 定义两个数组
call RANDOM_SEED()
call RANDOM_NUMBER(x) ! 给数组每一个元素赋值0-1的随机数
y = square(x) ! 调用逐元函数,给x的所有元素开平方
print *,"x"
do i = 1,size(x,1)
print *,(x(i,j),j=1,size(x,2)) ! 打印输出
enddo
print *,"y"
do i = 1,size(y,1)
print *,(y(i,j),j=1,size(y,2)) ! 打印输出
enddo
contains
elemental function square(x)result(res) ! 逐元函数体
real(8),intent(in) :: x
real(8) :: res
res = x * x
end function square
end program
上面是一个将数组中的每个元素都翻倍的 Element 函数示例。
1.7 抽象函数
在Fortran中,抽象函数是一种允许定义函数签名但不提供具体实现的机制。抽象函数通常与抽象接口结合使用,允许编写代码时指定函数的预期行为,而不必提供具体实现。这种机制对于构建可扩展和灵活的代码库非常有用,因为它允许用户根据需要提供自定义实现,而不必修改现有代码。
下面是Fortran中抽象函数的详细介绍:
1.7.1 抽象接口
抽象函数通常与抽象接口一起使用。抽象接口定义了函数的签名,但没有实现。这样,任何函数只要符合抽象接口定义的签名,就可以被视为抽象函数的候选实现。
abstract interface
function MyAbstractFunction(x)
real :: MyAbstractFunction
real, intent(in) :: x
end function MyAbstractFunction
end interface
1.7.2 模块化
抽象接口通常包含在模块中,以便在程序中其他地方使用。这种模块化的方法使得抽象函数能够在整个程序中被调用和实现。
module AbstractFunctions
implicit none
! 定义抽象接口
abstract interface
function MyAbstractFunction(x)
real :: MyAbstractFunction
real, intent(in) :: x
end function MyAbstractFunction
end interface
contains
! 其他子程序可以使用抽象函数
subroutine UseAbstractFunction(func, x)
procedure(MyAbstractFunction) :: func
real, intent(in) :: x
real :: result
result = func(x)
print *, "Result:", result
end subroutine UseAbstractFunction
end module AbstractFunctions
1.7.3 实现调用
具体的函数实现了抽象接口定义的签名,以满足抽象函数的要求。这些具体实现可以在程序的任何地方定义。
program test_abstract
use AbstractFunctions
implicit none
real :: input_value = 2.0
! 将抽象函数作为参数传递
call UseAbstractFunction(ConcreteFunction, input_value)
contains
! 具体实现的函数
function ConcreteFunction(x)result(res)
real, intent(in) :: x
real :: res
res = x ** 2
end function ConcreteFunction
end program
通过这种方式,Fortran中的抽象函数提供了一种灵活的机制,使得代码能够轻松地适应不同的需求和实现。
2 结语
这些是 Fortran 中常见的函数和子程序的类型和示例。它们可以帮助你编写模块化、可重用的代码。