前面有一次的教程我提到过,在Fortran中有很多种情况下定义函数需要定义接口(interface)之后才能够被调用,例如函数的返回值为数组,函数的参数不固定等。例如根据方位角得出该向量的值的函数实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1    function sphere2rect(theta, phi)
2 implicit none
3 real, intent(in) :: theta, phi
4 real :: sphere2rect(3)
5 sphere2rect(1) = sin(theta)*cos(phi)
6 sphere2rect(2) = sin(theta)*sin(phi)
7 sphere2rect(3) = cos(theta)
8 end function sphere2rect
9 program main
10 implicit none
11 interface
12 function sphere2rect(theta, phi)
13 real, intent(in) :: theta, phi
14 real :: sphere2rect(3)
15 end function sphere2rect
16 end interface
17 real, parameter :: pi=3.1415927
18 write(*,*) sphere2rect(pi/3, pi/6)
19 end program main

可以看出若是要在program中调用sphere2rect函数,则需声明一个接口interface,就是为了告诉编译器这个函数的输入输入参数分别是什么。前面也提到过,若是利用module则可以避免频繁的调用书写接口程序(隐式声明)。此段代码可简单的改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1    module funs
2 implicit none
3 contains
4 function sphere2rect(theta, phi)
5 implicit none
6 real, intent(in) :: theta, phi
7 real :: sphere2rect(3)
8 sphere2rect(1) = sin(theta)*cos(phi)
9 sphere2rect(2) = sin(theta)*sin(phi)
10 sphere2rect(3) = cos(theta)
11 end function sphere2rect
12 end module funs
13 program main
14 use funs
15 implicit none
16 real, parameter :: pi=3.1415927
17 write(*,*) sphere2rect(pi/3, pi/6)
18 end program main

若仅仅只想在program中使用这个函数,则可以直接将函数包含在program的contains中。类似的也可以包含在function或者subroutine中作为局域函数。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1    program main
2 implicit none
3 real, parameter :: pi=3.1415927
4 write(*,*) sphere2rect(pi/3, pi/6)
5 contains
6 function sphere2rect(theta, phi)
7 implicit none
8 real, intent(in) :: theta, phi
9 real :: sphere2rect(3)
10 sphere2rect(1) = sin(theta)*cos(phi)
11 sphere2rect(2) = sin(theta)*sin(phi)
12 sphere2rect(3) = cos(theta)
13 end function sphere2rect
14 end program main

另外为了减少重复编译,也可将函数定义在submodule中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1    module funs
2 implicit none
3 interface
4 module function sphere2rect(theta, phi)
5 implicit none
6 real, intent(in) :: theta, phi
7 real :: sphere2rect(3)
8 end function sphere2rect
9 end interface
10 end module funs
11 submodule(funs) funs_sub
12 implicit none
13 contains
14 module procedure sphere2rect
15 sphere2rect(1) = sin(theta)*cos(phi)
16 sphere2rect(2) = sin(theta)*sin(phi)
17 sphere2rect(3) = cos(theta)
18 end procedure sphere2rect
19 end submodule funs_sub
20 program main
21 use funs
22 implicit none
23 real, parameter :: pi=3.1415927
24 write(*,*) sphere2rect(pi/3, pi/6)
25 end program main

注意这里的interface不能定义在contains中。再submodule中函数实现的时候用的是procedure关键字,而不是function,若要用function关键字,则需把形参声明写完整。

第二个内容是利用interface去重载函数。若一族类似的函数可以被重载,则这族函数要么参数个数不同,要么变量类型不同。函数返回值不同是不能被重载的。例如几个加法函数的重载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1    module add_funs
2 implicit none
3 interface add
4 module procedure add_real, add_integer, add_3
6 end interface
7 contains
8 real function add_real(a, b)
9 implicit none
10 real :: a, b
11 add_real = a + b
12 end function
13 integer function add_integer(a, b)
14 implicit none
15 integer :: a, b
16 add_integer = a + b
17 end function
18 real function add_3(a, b, c)
19 implicit none
20 real :: a, b, c
21 add_3 = a + b + c
22 end function
23 end module add_funs
24 program main
25 use add_funs
26 implicit none
27 write(*,*) add(1,2)
28 write(*,*) add(2.,3.)
29 write(*,*) add(1.,2.,3.)
30 end program main

上述代码中的&符号是用来续行的,新标准中仅需要把续行符放在代码末尾就可以了。

如果仅仅是参数个数不同,还可利用optional属性来限定参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1    module add_funs
2 implicit none
3 contains
4 real function add(a, b, c)
5 implicit none
6 real :: a, b
7 real, optional :: c
8 add = a + b
9 if(present(c)) add = add+c
10 end function
11 end module add_funs
12 program main
13 use add_funs
14 implicit none
15 write(*,*) add(2.,3.)
16 write(*,*) add(1.,2.,3.)
17 end program main

其中的optional属性就是声明该参数是可缺省的。present函数是判断这个参数是否被赋值,若被赋值则返回.true.,否则.false.。