我希望 Fortran 支持流行的语法,比如
subroutine mysub( x, val = -1 )
integer, optional :: val
或者更多的 Fortran 风格
subroutine mysub( x, val )
integer, optional :: val = -1 !! not SAVE attribute intended
但这似乎不受支持(截至 2016 年)。所以一些解决方法需要由用户方完成......
就我而言,经过反复试验,我决定在可选的虚拟参数上附加一个下划线,所以做一些类似 (*)
subroutine mysub( x, val_)
integer, optional :: val_
integer val
其他人似乎喜欢相反的模式(例如,虚拟变量 => sep
、局部变量 => sep_
,请参见StringiFor中的split())。如这一行所示,设置默认值的最短方法是
val = -1 ; if (present(val_)) val = val_
但是因为即使这一行有点冗长,我通常会定义一个宏
#define optval(x,opt,val) x = val; if (present(opt)) x = opt
在一个通用的头文件中并将其用作
subroutine mysub( x, val_, eps_ )
integer :: x
integer, optional :: val_
real, optional :: eps_
integer val
real eps
optval( val, val_, -1 )
optval( eps, eps_, 1.0e-5 )
print *, "x=", x, "val=", val, "eps=", eps
endsubroutine
...
call mysub( 100 )
call mysub( 100, val_= 3 )
call mysub( 100, val_= 3, eps_= 1.0e-8 )
然而,我相信这仍然远非优雅,只不过是努力使其不易出错(通过在子例程的主体中使用所需的变量名)。
非常“大”的子例程的另一个解决方法可能是传递包含所有剩余关键字参数的派生类型。例如,
#define getkey(T) type(T), optional :: key_; type(T) key; if (present(key_)) key = key_
module mymod
implicit none
type mysub_k
integer :: val = -1
real :: eps = 1.0e-3
endtype
contains
subroutine mysub( x, seed_, key_ )
integer :: x
integer, optional :: seed_
integer :: seed
getkey(mysub_k) !! for all the remaining keyword arguments
optval( seed, seed_, 100 )
print *, x, seed, key% val, key% eps
endsubroutine
endmodule
program main
use mymod, key => mysub_k
call mysub( 10 )
call mysub( 20, key_= key( val = 3 ) )
call mysub( 30, seed_=200, key_= key( eps = 1.0e-8 ) ) ! ugly...
endprogram
这可能有点接近幕后某些动态语言所做的事情,但这又远非上述形式的优雅......
(*) 我知道使用 CPP 宏通常被认为是丑陋的,但 IMO 这取决于它们的使用方式;如果它们仅限于 Fortran 语法的有限扩展,我觉得使用是合理的(因为 Fortran 中没有元编程工具);另一方面,应该避免定义依赖于程序的常量或分支。另外,我想使用 Python 等来制作更灵活的预处理器(例如PreForM.py和fypp等)会更强大,例如,允许类似的语法subroutine sub( val = -1 )