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++一样,懂一些面向对象的内容还是有助于将代码写的更结构化一些。