我希望Fortran能支持像流行语法一样的东西
subroutine mysub( x, val = -1 )
integer, optional :: val
或者更像Fortran风格的写法
subroutine mysub( x, val )
integer, optional :: val = -1
但是据我所知(截至2016年)似乎不被支持。因此,用户需要自己进行一些解决方法...
在我的情况下,经过反复尝试,我最终选择给可选的虚拟参数添加一个下划线,这样做类似于 (*)
subroutine mysub( x, val_)
integer, optional :: val_
integer val
其他人似乎喜欢相反的模式(即,虚拟变量 => sep
,本地变量 => sep_
,例如请参见split()在StringiFor中)。如this line所示,设置默认值的最短方式是
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)
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 ) )
endprogram
这可能与某些动态语言在幕后执行的操作非常接近,但是这种形式仍然远离优雅...
(*) 我知道使用 CPP 宏通常被认为是丑陋的,但在我看来,这取决于它们的使用方式;如果它们仅限于有限的 Fortran 语法扩展,我认为使用是合理的(因为 Fortran 中没有元编程功能);另一方面,定义程序相关的常量或分支可能应该避免。此外,我想使用 Python 等更灵活的预处理器(例如
PreForM.py 和
fypp 等)可能会更加强大,例如允许像
subroutine sub( val = -1 )
这样的语法。