2016-06-27 7 views
1

実行時の入力に応じて、割り当て可能な配列Aをランク1,2または3にしたいプログラムを作成しようとしています。私はAの後続の操作が似ているので、私はこれをやりたいと思います。そして、Aで動作したときに望ましい結果が得られるモジュール手続きを持つインタフェースworkをモジュールで定義しました。Fortran:割り当て可能な配列のランクを選択する

program main 
implicit none 
integer :: rank,n=10 
real*8, allocatable :: A1(:) 
real*8, allocatable :: A2(:,:) 
read (*,*) rank 

if (rank.eq.1) then 
    allocate (A1(n)) 
else if (rank.eq.2) then 
    allocate (A2(n,n)) 
end if 

! operate on the array 
if (rank.eq.1) then 
    call work(A1) 
else if (rank.eq.2) then 
    call work(A2) 
end if 

end program 

その後、ifステートメントを必要としないよう何とか私は、Aのランクを選択することができれば、物事はずっと容易になるだろう:私は現在やっている何を

はこれです。たぶんこれは不可能ですが、すべての助けに感謝します。

+3

あなたが知っているように、実行時に配列のランクを割り当てることはできません。あなたはこれを回避するために起こりうる不具合の一つを示すスニペットを私たちに示しました。慎重に 'include 'を使うなど、他にもある。派生型の中にrank-1配列をラップして、それがrank-n(実行時にnを選択する)のように操作を書くことさえできます。しかし、なぜあなたがこれを望んでいるのかを私たちに教えれば、施設なしでプログラムを書く方法を教えてくれるかもしれません。 –

+0

ご返信ありがとうございます。私がこれをしたいのは、可能ならば、私のコードは(すべてのif文なしで)もっときれいに見えて、変更しやすいと思うからです。しかし、おそらく、私がやっていることには大惨事です。私のプログラムでは、1d時間依存型シュレディンガー方程式(TDSE)、2D TDSEを解くことができ、配列Aは波動関数の次元になります。派生型で提案したソリューションへの参照はありますか? – Lun

答えて

1

次のFortran標準(2015)は、select caseに似たselect rank構造を持っています。私の例では、仮定ランクのダミー変数のrank組み込みのselect case構成を使用しています。

module my_type 

    use, intrinsic :: iso_fortran_env, & 
     ip => INT32, & 
     wp => REAL64 

    implicit none 
    private 
    public :: MyType 

    type MyType 
    real (wp)    :: rank0 
    real (wp), allocatable :: rank1(:) 
    real (wp), allocatable :: rank2(:,:) 
    real (wp), allocatable :: rank3(:,:,:) 
    contains 
    procedure :: create => create_my_type 
    procedure :: destroy => destroy_my_type 
    end type MyType 

contains 

    subroutine create_my_type(this, array) 
    ! calling arguments 
    class (MyType), intent (in out) :: this 
    real (wp),  intent (in)  :: array(..) !! Assumed-rank dummy variable 

    ! local variables 
    integer (ip), allocatable :: r(:) 

    select case(rank(array)) 
    case (0) 
     return 
    case (1) 
     r = shape(array) 
     allocate(this%rank1(r(1))) 
    case (2) 
     r = shape(array) 
     allocate(this%rank2(r(1), r(2))) 
    case (3) 
     r = shape(array) 
     allocate(this%rank3(r(1), r(2), r(3))) 
    case default 
     error stop 'array must have rank 0,1,2, or 3' 
    end select 

    ! Release memory 
    if (allocated(r)) deallocate(r) 

    end subroutine create_my_type 


    subroutine destroy_my_type(this) 
    ! calling arguments 
    class (MyType), intent (in out) :: this 

    if (allocated(this%rank1)) deallocate(this%rank1) 
    if (allocated(this%rank2)) deallocate(this%rank2) 
    if (allocated(this%rank3)) deallocate(this%rank3) 

    end subroutine destroy_my_type 

end module my_type 

program main 

    use, intrinsic :: iso_fortran_env, only: & 
     ip => INT32, & 
     wp => REAL64 

    use my_type, only: & 
     MyType 

    implicit none 

    type (MyType) :: foo 
    real (wp)  :: a0, a1(42), a2(42,42), a3(42,42,42) 

    print *, rank(a0) 
    print *, rank(a1) 
    print *, rank(a2) 
    print *, rank(a3) 

    ! Allocate array of rank 3 
    call foo%create(a3) 

    print *, rank(foo%rank3) 
    print *, shape(foo%rank3) 
    print *, size(foo%rank3) 

    ! Release memory 
    call foo%destroy() 

end program main 
+0

はい、F2015では想定ランクの配列を使用できます。しかし、完全にFortranプログラムでの使用は非常に制限されているため、プログラムの流れを制御するにはあまり適していません。つまり、 'create_my_type'の' array'の値は自由に利用できないので、リテラル定数 '3'を渡すだけで同じ制御を得ることができます。 – francescalus

+1

@francescalus F2015は、さらなるC相互運用性TSを超える機能を追加します。 'select rank'は、' select type'と同様のモデルです(ただし、オブジェクト属性にはいくつかの違いがあります)。ブロックを先導するselect rank case文に対応するランクを持つ子ブロックに名前を用意します。あたかもそれが通常の配列であるかのように、特定のランクでそのオブジェクトを参照/定義することができます。しかし、このF201X機能の使用は今日でもかなり仮説的です。 – IanH

+0

追加情報ありがとう、@IanH。ドラフト/プロポーザルのコピーが最新であることを確認する必要があります。うまくいけば、私の最初のコメントは、より一般的な 'select rank'のやり方ではなく、この答えの例を参照するので意味があります。 – francescalus

1

配列をランク3に宣言します。下位配列が必要な場合は、関連するトレーリングディメンションをサイズ1に割り当てます。

real, allocatable :: array(:,:,:) 
... 
select case (desired_rank) 
case (1) ; allocate(array(n,1,1)) 
case (2) ; allocate(array(n,n,1)) 
case (3) ; allocate(array(n,n,n)) 
case default ; error stop 'bad desired rank' 
end select 

は、次に、あなたの希望のランクと一致しているarrayの連続したスライスを取得するために、部分配列を使用することができます。あるいは、配列に作用する関連プロシージャを記述して、ランク3の引数を取って、高次元のサイズの意味を認識させます。

+0

ありがとうございますが、これが私の問題を完全に解決するかどうかはわかりません。私は 'array'で動作するときに望ましい結果を出す手続き' work'(入力配列のランクに依存するサブルーチンを含みます)を持っています。配列セクションを使って配列の連続スライスを取得する場合、 'work'を使う前に配列スライスを保存する必要がありますか?私があなたの代わりの提案に従えば、私は3つの異なるケースを認識するように 'work'を定義しなければならないでしょう、基本的に私の元の投稿の' if'ステートメントをサブルーチンに移します。しかし、これは '選択ランク'に頼ることなく唯一の方法かもしれません。 – Lun

+0

現在、1つの汎用インターフェースの背後に3つの手順があります。私は "アレイスライスを保存する"という意味を知らない。 'array(:、i、j)'という構文を使って連続したランク1の配列スライスを作ることができます。場合によっては、上位の配列の処理には最後のランク以上の反復が含まれます。この場合、 'if'文は、下位配列を埋め込むためにこの種のスキームを使用すると、1回の反復で暗黙になります。ジェネリックインターフェイスを使用してプロシージャを呼び出さない場合は、シーケンスアソシエーションを使用することもできます。 – IanH

+0

もう一度ありがとうございます。しかし、1つのジェネリックインターフェイスの後ろに2つのプロシージャ(配列のランクに応じて)があり、それぞれの配列要素に数字を掛けた配列を出力します。あなたが提案したことをするには、スライスを選択するために 'if'ステートメントを作成する必要があるか、メインプログラムから外部ランクインデックスをループしてプロシージャを呼び出す必要があります(外部ループを並列化したいので望ましくない)。あなたの答えの 'case(1)'の 'array'だけがランク1であれば、すべてが簡単に動作します。 :) – Lun

関連する問題