Fortran也可以进行类似C++中的运算符重载。相应的关键字都是operator。关于函数动态绑定则类似于C/C++中的函数指针,在这里称之为过程指针,不过一个意思。

运算符重载

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
1    module typedef
2 implicit none
3 type nums
4 integer :: a, b
5 contains
6 procedure :: mul => mul_type
7 procedure :: add => add_type
8 procedure :: ass => ass_fun
9 generic :: operator(+) => add
10 generic :: operator(*) => mul
11 generic :: assignment(=) => ass
12 end type nums
13 interface operator(+)
14 module procedure add_right, add_left
15 end interface
16 contains
17 type(nums) function mul_type(this, type_b)
18 implicit none
19 class(nums), intent(in) :: this, type_b
20 mul_type%a = this%a * type_b%a
21 mul_type%b = this%b * type_b%b
22 end function mul_type
23 type(nums) function add_type(this, type_b)
24 implicit none
25 class(nums), intent(in) :: this, type_b
26 add_type%a = this%a + type_b%a
27 add_type%b = this%b + type_b%b
28 end function add_type
29 type(nums) function add_right(this, c)
30 implicit none
31 class(nums), intent(in) :: this
32 integer, intent(in) :: c
33 add_right%a = this%a + c
34 add_right%b = this%b + c
35 end function add_right
36 type(nums) function add_left(c, this)
37 implicit none
38 class(nums), intent(in) :: this
39 integer, intent(in) :: c
40 add_left%a = this%a + c
41 add_left%b = this%b + c
42 end function add_left
43 subroutine ass_fun(this, c)
44 implicit none
45 class(nums), intent(out) :: this
46 integer, intent(in) :: c
47 this%a = c
48 this%b = c
49 end subroutine ass_fun
50 end module typedef
51 program main
52 use typedef
53 implicit none
54 type(nums) :: nums1, nums2
55 nums1 = 2
56 nums2 = nums1 + 3
57 write(*,*) nums2
58 write(*,*) nums2*nums1
59 write(*,*) 6 + nums2
60 end program main

涉及到面向对象的内容,例子可能会相对较长,这里我也是想用尽量短的篇幅,讲清楚问题。Fortran中运算符是通过operator的interface绑定过程procedure来实现的。例如代码中的add_left和add_right同时重载到了算符+上。这里重载+是为了使得nums类可以和一个整型数字去做加法,至于为什么这里需要定义两个函数,这是为了避免左加和右加的问题(56行和59行代码),在C++中类的左加通常是通过友元函数来实现的(friend)。若是算符不需要由两个以上的procedure进行重载重载,则可以直接用genetic绑定到type中,但是首先需要利用procedure声明这个函数过程。例如乘法的重载(6行和10行缺一不可)。比较特殊的是这里对于等号的重载关键字不是operator,而是assignment,重载的是subroutine而不是function。注意这里的要用于重载的function的变量必须显式声明为intent(in),subroutine中的一个为intent(out),一个为intent(in)。

函数动态绑定

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
30
31
32
33
34
35
36
37
38
39
40
41
1    module typedef
2 implicit none
3 type nums
4 real :: a
5 procedure(funcs), public, nopass, pointer :: func => null()
7 contains
8 procedure :: doit
9 end type nums
10 interface
11 real function funcs(x)
12 implicit none
13 real :: x
14 end function funcs
15 end interface
16 contains
17 real function doit(this)
18 implicit none
19 class(nums) :: this
20 doit = this%func(this%a)
21 end function
22 real function square(x)
23 implicit none
24 real :: x
25 square = x*x
26 end function square
27 real function cube(x)
28 implicit none
29 real :: x
30 cube = x*x*x
31 end function cube
32 end module typedef
33 program main
34 use typedef
35 implicit none
36 type(nums) :: nums1
37 nums1%a = 5.0
38 nums1%func => square
39 write(*,*) nums1%doit()
40 nums1%func => cube
41 write(*,*) nums1%doit()
42 end program main

这里定义了两个函数,分别是平方函数和立方函数,为了能动态绑定函数,这里我定义了一个过程指针func并将他绑定在了接口funcs上,这里的接口仅需要声明形参变量类型与函数返回值类型即可,这里动态绑定的是两个输入输出参量类型个数都一致的函数。nopass关键字是声明该过程为静态过程,类似Python中的staticmethod方法,这里不能含有”this”变量。初始化指向null是为了避免野指针。动态绑定是通过=>来完成的。

写在最后

关于Fortran中面向对象的内容对于普通科研工作者而言似乎意义不大,尤其是若是在服务器上计算,gcc版本可能比较低以至于还不支持这些新的特性,但是就像C++一样,懂一些面向对象的内容还是有助于将代码写的更结构化一些。