Fortran有两种实现函数调用的方式,分别是通过function和subroutine完成的。这里分别对应于C/C++中的有返回值的函数和无返回值的函数。需要注意的一点是Fortran中传参默认是传址,类似C/C++中将函数参数都定义为引用一样。下属文字中未做特殊说明,”函数”均是function和subroutine的统称。

function

function的结构为:

1
2
3
4
5
返回值类型 修饰符 function 函数名(参数列表)
参数声明
函数体
return
end function 函数名

返回值类型就是基本数据类型。修饰符是用来声明函数属性的,常见属性关键字有recursive(递归)、elemental(数组函数)和pure(纯函数),这些放到以后讲解。其中的return若在函数末尾就可省略,类似的主程序中program也有类似关键字stop,若放在末尾一样也可以省略。return和stop一般用来中途退出函数和结束程序。函数内的函数的返回值变量名和定义的函数名一致,若函数名过长,可以使用result关键字起一个简短的函数名称(仅在函数内有效)。另外函数形参和虚参的类型要匹配。例子:

1
2
3
4
5
6
7
8
9
10
11
1    real function add(x, y) result(ans)
2 implicit none
3 real :: x, y
4 ans = x + y
5 end function add
6 program main
7 implicit none
8 real, external :: add
9 real :: a, b
10 write(*,*) add(a, b)
11 end program main

上述定义了一个加法函数,并起别名为ans,Fortran不像C/C++中用return来返回变量值,而是赋值给了返回值变量。另外主程序或者函数中要调用该函数则要用external关键字进行声明,表明这个量是一个函数(这里比较繁琐,但利用模块module可简化,这个之后会提及)。由于Fortran中传参是传值的方式进行的,所以函数虚参若在函数的执行过程中发生了变化,则函数形参也会发生变化。这在函数体比较大的时候容易出错。为了避免这种情况的发生,可以将上述代码改为:

1
2
3
4
5
1    real function add(x, y) result(ans)
2 implicit none
3 real, intent(in) :: x, y
4 ans = x + y
5 end function add

其中加入了intent变量属性,括号中的内容有三个in、out和inout,分别代表函数参量是”输入的”、”输出的”和”可双向传输的”。intent也不是非加不可,但是有利于程序的查错。函数参数若加了intent(in),则在函数中该参量是不能发生变化的,若发生变化,编译的时候就会报错,类似C/C++将函数参量声明const。若变量属性为intent(out)或intent(inout),因为要返回值,从而对应形参必须是一个”变量”(例:add(a,b)),而不是一个”数字”(例:add(1,2))。关于out和inout的区别三言两语难解释清楚,有兴趣可以参考(https://stackoverflow.com/questions/29737367/difference-between-intentout-and-intentinout),不过intent(out)一般用在纯函数和运算符重载那里,那些地方要强制用intent属性。

subroutine

subroutine的结构为:

1
2
3
4
修饰符 subroutine 函数名(参数列表)
参数定义
函数体
end subroutine 函数名

可以遇见subroutine只能通过参数去返回计算结果(common和module除外),通常用于多返回值的情况。例如计算三维向量的两个方位角:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1    subroutine rect2sphere(vec, theta, phi)
2 implicit none
3 real, intent(in) :: vec(3)
4 real, intent(out) :: theta, phi
5 theta = acos(vec(3)/norm2(vec))
6 phi = atan2(vec(2),vec(1))
7 end subroutine rect2sphere
8 program main
9 implicit none
10 real :: vec3(3), t, p
11 vec = [1, 2, 3]
12 call rect2sphere(vec3, t, p)
13 write(*,*) t, p
14 end program main

其中acos是反余弦,atan2函数是反正切,norm2还是用于求向量的模。subroutine通过call关键字进行调用。

最后需要指出的是,当函数返回值为数组、函数参数个数不定、返回值为指针等情况。若要调用该函数,则需要在主程序或函数内声明接口interface。另外也可在主程序、函数和模块内定义函数(contains),并且这种定义方式不需要显式声明接口。这些放到以后讲解。