16

Fortran では、デフォルトの引数を定義できます。ただし、オプションの引数が存在しない場合は、設定することもできません。デフォルト値を持つキーワード引数として引数を使用する場合、これは次のような厄介な構造につながります

PROGRAM PDEFAULT 

  CALL SUB
  CALL SUB(3)

CONTAINS 
  SUBROUTINE SUB(VAL)
    INTEGER, OPTIONAL :: VAL
    INTEGER :: AVAL ! short for "actual val"

    IF(PRESENT(VAL)) THEN
       AVAL = VAL
    ELSE 
       AVAL = -1   ! default value 
    END IF

    WRITE(*,'("AVAL is ", I0)') AVAL
  END SUBROUTINE SUB

END PROGRAM PDEFAULT

VAL個人的には、の代わりに誤って入力してしまうという問題によく遭遇しましたAVAL。つまり、インターフェイス内の変数名と、コードで使用される初期化された値との間の接続が切断されて、ランタイム バグが発生する可能性があります。

デフォルト値でオプションの引数を使用するより洗練された方法はありますか?

次のように書くとより自然に感じるでしょう。

IF(NOT(PRESENT(VAL))) VAL = -1 

VALvsのAVAL混乱を避けるためです。おそらく、Fortran は参照によって引数を渡すためVAL、ステートメントに が存在しない場合CALL、メモリが関連付けられておらずVALVAL = -1segfault が発生するため、これは有効ではありません。

4

6 に答える 6

12

You described the situation rather well. There is no other way I am aware off and that is standard conforming. The pattern with a local variable named similarly is what people often use. The other option is to just put if (present()) else everywhere, but that is awkward.

The point is that they are optional arguments, not default arguments. Fortran doesn't have default arguments. The may have been better, but that is not what the committee members have chosen in the 80s when preparing Fortran 90.

于 2016-06-09T11:26:50.230 に答える
7

OPTIONALこれも調べているうちに、提案された例のようにand属性を使用して実際に実行できることがわかりましたVALUE(少なくとも gfortran では、さまざまなコンパイラがそれをどのように処理するかはわかりません)。例えば:

PROGRAM PDEFAULT 

  CALL SUB
  CALL SUB(3)

CONTAINS 
  SUBROUTINE SUB(VAL)
    INTEGER, OPTIONAL,VALUE :: VAL

    IF(.NOT. PRESENT(VAL)) VAL = -1 ! default value

    WRITE(*,'("VAL is ", I0)') VAL
  END SUBROUTINE SUB

END PROGRAM PDEFAULT

これは gfortran のバージョン 4.9 で実装されました。そして、これは引数渡し規則のドキュメントに関連する説明です:

OPTIONAL 仮引数の場合、VALUE 属性を持つタイプ INTEGER、LOGICAL、REAL、および COMPLEX のスカラー仮引数を除き、引数がない場合は NULL ポインターで示されます。これらの場合、非表示のブール引数 (logical(kind=C_bool),value) を使用して、引数が存在するかどうかを示します。

また、この議論は歴史的文脈として興味深いものでした。

おそらく、より知識のある誰かが、これを行うことが悪い考えであるかどうかについてコメントするかもしれません (コンパイラーに依存することは別として) が、少なくとも額面どおり、それは良い回避策のように思えます。

この動作は Fortran 標準の一部ではなく、特定のコンパイラの実装に依存することに注意してください。たとえば、ifort (バージョン 16.0.2) を使用すると、サンプル コードはセグメンテーション違反になります。

于 2016-10-19T07:04:26.940 に答える
1

私は確かにほとんどの状況でそうすることを推奨しませんが (実際、状況によってはそうすることができません)、インターフェイスを使用して、オプションの引数を使用するのではなく、異なる必須引数を持つ複数のルーチンに単一のエントリ ポイントを提供することがあります。たとえば、コードは次のように記述できます

MODULE subs
  implicit none
  public :: sub

  interface sub
    module procedure sub_default
    module procedure sub_arg
  end interface
 contains
  SUBROUTINE SUB_arg(VAL)
    INTEGER :: VAL
    WRITE(*,'("VAL is ", I0)') VAL
  END SUBROUTINE SUB_arg

  SUBROUTINE SUB_default
     integer, parameter :: default = 3
     CALL SUB_arg(default)
  END SUBROUTINE SUB_default
END MODULE SUBS

PROGRAM test
   use subs, only: sub
   call sub
   call sub(5)
END PROGRAM TEST

繰り返しますが、このアプローチはお勧めしませんが、デフォルトのように見えるものを提供する別の方法として、とにかくそれを含める必要があると考えました。

于 2016-06-09T18:39:21.790 に答える
1

別の可能性は、ローカル変数名をオプションの引数と同じ名前の変数に関連付ける関連付けブロックを使用することです。

SUBROUTINE SUB(VAL)
INTEGER, OPTIONAL :: VAL
INTEGER :: AVAL ! short for "actual val"

IF (PRESENT(VAL)) THEN
    AVAL = VAL
ELSE 
    AVAL = -1   ! default value 
END IF

ASSOCIATE (VAL => AVAL)
    WRITE(*,'("VAL is ", I0)') VAL
END ASSOCIATE

END SUBROUTINE SUB

理想的ではありませんが、引数とルーチンの本体で同じ変数名を使用できます。オプションの引数のデフォルト値の欠如に対処するために、私が書いただらしないコードの量を考えるとぞっとします-F202Xにロールバックします。

于 2019-07-05T13:26:57.270 に答える
0

Fortran が次のような一般的な構文をサポートすることを願っています

subroutine mysub( x, val = -1 )
integer, optional :: val

またはよりFortranスタイルで

subroutine mysub( x, val )
integer, optional :: val = -1     !! not SAVE attribute intended

しかし、これはサポートされていないようです (2016 年現在)。そのため、ユーザー側で何らかの回避策を講じる必要があります...

私の場合、試行錯誤の末、任意の仮引数にアンダースコアを1つ付けることに落ち着いたので、(*)のようにしました。

subroutine mysub( x, val_)
integer, optional :: val_
integer val

他の人は反対のパターンを好むようです (つまり、ダミー変数 => sep、ローカル変数 => 。たとえば、 StringiFor のsplit() を参照)sep_この行に見られるように、デフォルト値を設定する最短の方法は

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.pyfyppなど) を作成すると、より強力になると思います。たとえば、次のような構文を許可します。subroutine sub( val = -1 )

于 2016-06-12T19:21:17.477 に答える