Fortran配列受け渡し時の次元の一致と中身の挙動について

このQ&Aのポイント
  • Fortran90のコンパイラifortを使用して、配列の受け渡しについて調査しています。通常、メインプログラムとサブルーチン間で配列を受け渡す場合は、次元を揃えて渡すことが一般的です。しかし、あるコードでは2次元配列を渡して1次元配列で受け取っていました。テストコードを作成して実行した結果、コンパイルは通りましたが、サブルーチン側ではすべての要素が0で置き換えられるという結果でした。これはコンパイラの違いによるものでしょうか?
  • 質問をまとめると、次元の異なる配列の受け渡しが可能かどうか、そしてその場合の配列の中身について知りたいです。
  • よろしくお願いします。
回答を見る
  • ベストアンサー

fortran 配列受け渡し時の次元の一致

fortran90、コンパイラはifortです。 普通メインプログラムとサブルーチン間での配列の受け渡しは、次元を揃えて渡すと思います。 とあるコード(以後コードA)を読んでいると、2次元配列を渡し、1次元配列で受け取っていました。 例 program test1 integer :: a(3,3) call testsub(a) end program subroutine testsub(b) integer :: b(9) end subroutine これが受け取り側でどう処理されているのかわからず、調べるために適当なテストコードを書きました。 a 123 456 789 ↓ b 123456789 になるとか 結果、コンパイルは通ったのですがサブルーチン側では全て0で置き換えられてしまいました。 コードAはpgiかなんかでコンパイルしていたようなのでコンパイラの違いでしょうか? よくわらなかったので質問させて頂きました。 質問をまとめますと、 (1)次元の異なる配列の受け渡しができるかどうか (2)その場合中身はどうなるか よろしくお願いします。 ---以下テストコード--- program testa implicit none integer :: a(3,3),i,j do i=1,3 do j=1,3 a(i,j)=j+(i-1)*3 enddo enddo do i=1,3 do j=1,3 write(6,*) a(i,j) enddo enddo call sub1(a) end program subroutine sub1(b) integer :: b(9),i do j=1,9 write(6,*) b(i),'sub' enddo end subroutine

質問者が選んだベストアンサー

  • ベストアンサー
  • f272
  • ベストアンサー率46% (7994/17083)
回答No.1

(1)次元の異なる配列の受け渡しができるかどうか 昔はできたんだけどねえ。 (2)その場合中身はどうなるか 下記を実行してみればわかる。 program testa implicit none interface subroutine sub1(b) integer b(:) end subroutine end interface integer :: a(3,3),i,j do i=1,3 do j=1,3 a(i,j)=j+(i-1)*3 enddo enddo do i=1,3 do j=1,3 write(6,*) a(i,j) enddo enddo !call sub1(a) call sub1(reshape(a,(/3*3/))) end program subroutine sub1(b) integer :: b(:),i do i=1,ubound(b,1) write(6,*) b(i),'sub' enddo end subroutine ちなみに do i=1,3 do j=1,3 write(6,*) a(i,j) enddo enddo こういうアクセスの仕方はやめた方がいいよ。推奨されるのは do j=1,3 do i=1,3 write(6,*) a(i,j) enddo enddo この順です。アクセスの効率が異なります。

naeba123
質問者

お礼

サンプルありがとう御座います。 無事解決しました。 また、doループのアクセス効率に関するご指摘もありがとうございました。 勉強になりました。

その他の回答 (1)

  • boogeyman
  • ベストアンサー率35% (163/462)
回答No.2

subroutine sub1(b) integer :: b(1),i  か integer :: b(*),i にすれば、 b(1) ← a(1,1) b(2) ← a(2,1) b(3) ← a(3,1) b(4) ← a(1,2) b(5) ← a(2,2) b(6) ← a(3,2) b(7) ← a(1,3) b(8) ← a(2,3) b(9) ← a(3,3) のようなに数値が受け渡されると思います。

naeba123
質問者

お礼

どうやら私の環境ではできないようです。 Segmentation fault になりました、、、 変数の中身の置換され方はわかりやすかったです。 ありがとうございました。

関連するQ&A

  • FORTRAN subroutineと配列と繰り返し

     以下のように二次元配列の場合でsubroutineを使うときに、主プログラムで2重Doループ(iとj)で繰り返しをしているのですが、すでにsubroutineでDoループ(i)を用いて計算しています。これではsubroutineの利点をうまく使えていないと思うのですが、subroutineを使って配列、Doループをきれいにする方法をどなたか教えていただけませんか。  実際は4重ループ、4次元配列なので、プログラムをわかりやすくするためにサブルーチンを使いたいと思っています。 -------------------------------------------------------------- program S real,dimension(5,5) :: B real,dimension(5) :: A integer :: i,j do j=1,5 CALL sub1(A) do i=1,3 B(i,j)=A(i)*j write(*,*) B(i,j) end do end do end program S subroutine sub1(A) real,dimension(5) :: A integer :: i do i=1,3 A(i)=3.*i end do end subroutine sub1

  • fortran errorについて

    fortranを勉強していたのですがエラーがでてしまい、何時間かけても理解できなかったので質問させてください。 以下プログラム program test !ここからメインルーチン !前準備 配列の用意 implicit none integer N integer,dimension(0:N,0:N) :: A integer :: i,j,k read * ,N !初期状態の代入 do i=0,N do j=0,N A(i,j)=0 end do end do do i=N/2,N-1 A(N/2,i)=1 end do do i=N/2,N-1 A(N/2+1,i)=-1 end do !ループ 50回ループさせる do k=0,50 !状態の表示 call visualize !サブルーチン visualize subroutine visualize do i=0,N do j=0,N if(A(i,j)== 1) write(*,'(A1)',advance='NO') "*" if(A(i,j)== 0) write(*,'(A1)',advance='NO') " " if(A(i,j)==-1) write(*,'(A1)',advance='NO') "+" end do write(*,*) end do !end subroutine visualize call insert !サブルーチン insert subroutine insert do i=0,N do j=0,N if(A(i,j)== 1) A(i,j)=-1 if(A(i,j)== 0) A(i,j)=max(0,A(i-1,j),A(i,j-1),A(i,j+1),A(i+1,j)) if(A(i,j)==-1) A(i,j)=0 end do end do !end subroutine insert end do end program test これでコンパイラすると In file test.f90:48 subroutine visualize 1 Error: Unclassifiable statement at (1) In file test.f90:69 subroutine insert 1 Error: Unclassifiable statement at (1) とでます いろいろ調べたのですが全くわかりませんでした できればよろしくお願いします

  • fortran allocateを使って配列宣言を

    今変数 aについて考えています.aは i,jの2次元の座標におけるデータです. a(1,1)は10個配列を持ちたい a(1,2)は5配列を持ちたい a(i,j)はn個配列を持ちたい このような場合どのように配列を定義すれば良いのでしょうか? 例えば 2次元の大きさが3x3の場合で,それぞれの位置に配列したいデータ個数をnとします. nには既に個数が定義されているとします.このとき aの配列は nを使ってどのように定義すれば良いのでしょうか? integer n(3,3) integer i,j real, allocatable :: a(:,:,:) do j=1,3 do i=1,3 n(i,j)=i*j end do end do do j=1,3 do i=1,3 allocate (a(n(i,j),3,3)) end do end do では aの宣言が重複するためエラーになってします. 何方か良い方法を教えて下さい.

  • プログラムでの配列の渡し方

    ここではフォートランの問題としてお尋ねします。(フォートランのカテゴリがないのでプログラムの専門家に聞けそうなカテゴリに質問してます。) dimension a(100,100) ... call abc(a) ... stop end subroutine abc(b) dimenison b(100,1) ... return end というようなプログラムがあります。メインの方でa(100,100)と宣言してサブルーチンではb(100,1)で受けるというような処理です。 このようにメインとサブで配列のサイズが異るのはどのようなレベルで許容されるものでしょうか。考え方がわからないのでお尋ねします。私は厳密にサイズを合わせると思っていました。しかしそうだとサブルーチンの使い回しができなくなります。このような問題はC言語の配列の先頭のアドレス云々という問題と似ているのですが。2次元配列ということがわかっていて片方のサイズがわかると自ずからもう片方のサイズがわかるということなのでしょうか。 まとめますと、以下のような点がわからないということです。 1.メインとサブで配列サイズが異なっても問題ないやり方 2.サブ側が動的に対応できるようにするプログラムの書き方(同じサブルーチンだけど、呼び出すメイン側の配列サイズに自動で対応する方法) *などを使うのだろうと思いますが。 この部分はしっかり理解しないと大怪我するところなので確認したいと思っています。 (実験して試すというのではなく、仕様としてどうかということですが) よろしくお願いします。

  • fortranを用いた行列の掛け算について

    行列の掛け算についてなのですが、ある行列aを2乗した行列bを求める場合は以下のようなプログラムを書けば出来たのですが、これを3乗以上に拡張するためにはどうしたらよいのでしょうか? ______do i=1,3 ________do j=1,3 __________b(i,j)=0.D0 ____________do k=1,3 ______________b(i,j)=b(i,j)+a(i,k)*a(k,j) ____________enddo ________enddo ______enddo

  • 【Perl】サブルーチンから多次元配列を戻したい

    【Perl】サブルーチンから多次元配列を戻したい サブルーチン内で作った2次元配列と1つの変数(例:@listと$i)をリターンしたいのですが、どのようにすれば良いでしょうか? ↓サブルーチン(calc) return (@list, $i); ↓メイン my $a = 12; my (@list, $i) = &calc($a); 間違っていると思いますが、現時点でのプログラムを一部書きました。 ご回答よろしくお願いします!

    • ベストアンサー
    • Perl
  • fortran 配列

    はじめまして。fortranを勉強しているのですが割付け配列がよく理解できないので教えていただきたいのですが。以下に自分で作った簡単なプログラムを実行していたのですが正しい値になりません。どこが間違っているのでしょうか?教えて下さい。よろしくお願いします。 program list2_3 implicit none real(8), allocatable :: u(:), v(:) integer :: i, n = 2 real(8) dotp allocate (u(n), v(n)) u(1:2) = (/1.2d0,3.4d0/) v(1:2) = (/4.1d0,2.6d0/) dotp = 0.0d0 do i = 1, n dotp = dotp + u(n) * v(n) enddo deallocate (u, v) write(*,*) ' dot product =' , dotp end program list2_3

  • fortran 途中まで考えたのですが。。。。

    エラトステネスのふるい(素数の倍数を除いていって残ったのが素数)で3桁の素数を求めて表示したいです。 私が考えたのは、 1・2~99までの数を素数かどうか調べて、素数を配列に入れていく 2・100~999まで素数の配列の中の数で割って、割り切れたらおしまい。割り切れなかったら表示していく ということです。 しかし下のプログラムではうまく素数配列ができていないようなのです。 ここまでかなり時間がかかったのでこのプログラムに手をいれて これ以外におかしくなるところもどこを直せばいいのか教えてくださるとうれしいです。 C C q223.f C PROGRAM q223 C IMPLICIT NONE C INTEGER N,i,K,s,l REAL a(99),b(99),c(99),X,Y C real M C a(1)=2 a(2)=3 l=2 C DO N=2,99,1 M=N**(0.5) S=M DO i=2,S,1 K=MOD(N,i) IF(K ==0)THEN exit ELSE IF(K /=0)THEN l=l+1 a(l)=N ENDIF ENDDO ENDDO C do N=100,999 do l=1,99 X=a(l) Y=N/X if(Y==0)then exit else if(Y/=0)then write(*,*)N end if end do end do c end よろしくおねがいします

  • fortran 行列ベクトル積

    行列ベクトル積を計算するプログラムを下のように書いたのですが、実行した結果の答えが実際計算した答えと異なります。初期の要素の設定がおかしいのでしょうか?教えて下さい。よろしくお願いします。 program list2_14 implicit none integer , parameter :: n = 2 real(8) a(n,n), x(n), y(n) integer i, j, k, l a(1,1:2) = (/1.2d0,3.4d0/) a(2,1:2) = (/5.6d0,7.8d0/) x(:) = (/9.0d0,10.0d0/) do i = 1, n y(i) = 0.0d0 do j = 1, n y(i) = y(i) + a(i,j) * x(j) enddo enddo do k = 1, n write(*,*) (a(k,l), l = 1, n) enddo write(*,*) x(:) write(*,*) y(n) end program list2_14 実行結果 1.2 3.4 5.6 7.8 9. 10. 128.4

  • VB.NET 2次元配列

    二次元配列に関して質問があります。 二次元配列を定義します。 Dim a(,) As Integer = _ {{0, 0, 0, 0, 0, 0, 1, 1, 1}, _ {0, 0, 0, 0, 0, 1, 0, 0, 1}, _ {0, 0, 0, 0, 0, 0, 1, 1, 1}} 一次元配列を定義します。 Dim b() As Integer 一次元配列のb()に二次元配列a(,)の {0, 0, 0, 0, 0, 0, 1, 1, 1}の部分を 入れようとした場合、 どのように記載すればよろしいでしょうか? イメージ的には b = a(0) で出きると思ったのですが、 構文エラーとなってしまいます。 よろしくお願いします。