関連情報

Fortran Builder
ナビゲーション:前へ   上へ   次へ

12 配列

Fortranが数値計算に適している理由の一つに、「配列操作が簡単に行える」ということがあげられます。

12.1 配列の宣言

配列の利用にはまず宣言が必要です。以下に1次元、2次元、3次元の場合の 配列の宣言例を示します。配列の大きさを括弧内に記述するかもしくはdimension属性を指定します。
  real,dimension(5) :: a        ! dimension属性で指定する例。1次元配列5つの成分を持つ配列aを宣言
  real a(5), b(6), c(7)         ! このように記述することも可能です
  integer,dimension(3,3) :: d   ! 2次元配列3x3を持つ整数配列dを宣言
  complex,dimension(2,2,2) :: e ! 3次元配列2x2x2を持つ複素数配列eを宣言

Fortranではデフォルトで1〜指定した大きさのインデックスが使われます。(CやJava等のように0〜指定した大きさ-1ではありません。)

1からではなく任意の下限値を用いたい場合には下記の例のように宣言時に下限を指定することも可能です。

  real a(0:4)                 ! CやJavaのように0から始まり4まで合計で5つの成分を持つ配列を宣言する例
  real,dimension(3:5) :: b    ! dimension属性で指定する例。1次元配列3つの成分(3〜5)を持つ配列を宣言する例

補足:
Fortranの多次元配列は列優先 (Column Major) です。(C/C++言語では行優先) 例えば3行4列の2次元整数配列は integer a(3,4) のように宣言され、メモリ上には以下の順番で数値が格納されます。

a(1,1)
a(2,1)
a(3,1)
a(1,2)
a(2,2)
a(3,2)
a(1,3)
a(2,3)
a(3,3)
a(1,4)
a(2,4)
a(3,4)

参考図

12.2 配列に初期値を設定する

配列に初期値を設定する方法はいくつかあります。 ここでは宣言した配列のすべての値を同じ値に初期化したい場合の方法を紹介します。 配列のすべての成分を同じ値に初期化したい場合には以下の例のように変数宣言に=を用いて初期値を指定します。
  real :: a(100) = 0                ! 配列aのすべての成分を0にする
  integer,dimension(3,3) :: b = 1   ! 配列bのすべての成分を1にする

配列の各成分に異なる初期値を設定したい場合には以下のようにdata文を用いることもできます。

  integer i(4)
  data i/1,10,100,1000/  ! i(1)=1, i(2)=10, i(3)=100, i(4)=1000をそれぞれ設定する

またその他に以下のセクションで説明される配列構成子を利用して初期値の指定を行うことも可能です。

12.3 配列構成子とreshape組込み関数

配列に値を与える方法はいくつかありますが、配列構成子(/配列構成項目並び/)を用いる方法をここに示します。

配列構成子を用いると1次元の配列を構築することができます。例えば以下のように用います。

  integer a(5)
  a = (/ 1, 2, 3, 4, 5 /)

また、例えば以下のように初期値の指定にも利用可能です。

  integer :: a(5) = (/ 1, 2, 3, 4, 5 /)
  real :: b(6) = (/2., 4., 6., 8., 10., 12. /)

更に上記と同じ事を行う以下のような指定方法も許されています。(配列構成DO形反復と呼ばれる)

  integer :: a(5) = (/ (i,i=1,5) /)  ! 1,2,3,4,5となる
  real :: b(6) = (/ (i*5,i=1,6) /)   ! 5,10,15,20,25,30となる

配列構成子は1次元の配列を作成することができるものですが、 2次元以上の配列に値を指定したい場合にはreshape組込み関数が利用可能です。 reshape組込み関数に配列構成子で1次元配列を与え、指定する形状の多次元配列を作成することが可能です。

一番目の引数に元となる配列を与え、2番目の引数にほしい形状を指定します。 以下に例を示します。

  real :: a(2,3) = reshape( (/1., 2., 3., 4., 5., 6./), (/2,3/) )
    は以下のように設定されます。
  1 3 5
  2 4 6

  冗長的な記述が好ましくない場合には2番目の引数に shape 組込み関数を利用して以下のよう指定します。
  real :: a(2,3) = reshape( (/1., 2., 3., 4., 5., 6./), shape(a) )

  DO系反復による記述例を以下に示します。
  integer :: b(4,3) = reshape( (/ (i,i=1,12) /), shape(b) )
    は以下のように設定されます。
  1 5 9
  2 6 10
  3 7 11
  4 8 12

12.4 配列全体の代入や算術

配列全体への代入や算術を行うことが可能です。 例えば一次元配列 a のすべての成分を 10 にする場合には以下のように行います。
  integer a(3)
  a = 10

(補足) 多次元配列であっても配列全体が対象となります。

aのすべての成分に 1 を足す場合には以下のように記述します。

  a = a + 1

同様に配列 ab のそれぞれの成分を足しあわせる場合には 以下のように行うことが可能です。

  integer,dimension(3) :: a, b, c
  ...
  c = a + b

(補足) 配列は同じ形状(大きさ、次元)でなければなりません。

また出力も例えば以下のように行うことが可能です。

  print *, a   ! 配列全体が出力される

出力例:
  11 11 11

12.5 配列の各成分へのアクセス

配列の各成分に値を代入したり、もしくは各成分の値を取得するには 以下のように添字で成分を指定して行います。

[ array1d.f90 ] - 1次元配列の各成分を参照するサンプル
program array_1d
  implicit none
  integer,dimension(3) :: a
  a(1) = 10
  a(2) = 20
  a(3) = 30
  print *, a
end program array_1d

出力例:
 10 20 30

2次元配列の場合の例を以下に示します。

[ array2d.f90 ] - 2次元配列の各成分を参照するサンプル

program array_2d
  implicit none
  integer :: a(2,2)
  a(1,1) = 10
  a(2,1) = 20
  a(1,2) = 30
  a(2,2) = 40
  print *, a(1,1), a(2,1), a(1,2), a(2,2)
end program array_2d

出力例:
 10 20 30 40

12.6 ★ 練習課題:テスト得点の入力と表示

生徒数を入力後に、すべての生徒のテスト点数を入力し、それが完了してから すべての点数を表示するプログラムを作成して下さい。 この際に生徒数の最大は10を超えないものとします。

処理手順例

  1. 変数を宣言する
     例)integer i, n, scores(10) ! 最大10人
  2. 生徒数を入力してほしい旨を画面に出力する(print文)
  3. 生徒数をキーボードから入力する(read文)
  4. 以下の2アイテムを人数分繰り返す(do文)
  5. テスト点数を入力してほしい旨を画面に出力する(print文)
  6. テスト点数をキーボードから入力する(read文)
  7. 以下の1アイテムを人数分繰り返す(do文)
  8. テスト点数を表示する
実行例:
 Please enter number of students:
5
 Enter score of student # 1
75
 Enter score of student # 2
82
 Enter score of student # 3
94
 Enter score of student # 4
63
 Enter score of student # 5
58
 Student # 1 = 75
 Student # 2 = 82
 Student # 3 = 94
 Student # 4 = 63
 Student # 5 = 58
[ kadai-scores.f90 ] - テスト得点の入力と表示を行うプログラム例

12.7 配列に関連する主な組込み手続

配列に関連する主な組込み手続を以下に示します。

12.7.1 組込み手続 sum, maxval, minval

sum(配列変数名)は指定される配列の各成分の合計を計算します。 minval(配列変数名)とmaxval(配列変数名)は配列内の成分の最小値と最大値をそれぞれ求めます。 以下に使用例を示します。

[ array-intrinsics.f90 ] - 配列の組込み手続:sum/min/max
program array_intrinsics
  implicit none
  integer a(3)
  a(1) = 5
  a(2) = 10
  a(3) = 2
  print *, "sum=", sum(a)
  print *, "min=", minval(a)
  print *, "max=", maxval(a)
end program array_intrinsics

出力例:
 sum= 17
 min= 2
 max= 10

12.7.2 組込み手続 lbound, ubound

lbound(配列変数名,次元)とubound(配列変数名,次元)はそれぞれ 指定の配列の指定の次元の下限と上限を求めます。

[ array-intrinsics2.f90 ] - 配列の組込み手続:lbound/ubound
program array_intrinsics2
  implicit none
  integer a(3), b(3:5), c(8,5:10)
  print *, "a(", lbound(a,1), ":", ubound(a,1), ")"
  print *, "b(", lbound(b,1), ":", ubound(b,1), ")"
  print *, "c(", lbound(c,1), ":", ubound(c,1), ",", lbound(c,2), ":", ubound(c,2), ")"
end program array_intrinsics2

出力例:
 a( 1 : 3 )
 b( 3 : 5 )
 c( 1 : 8 , 5 : 10 )

12.7.3 組込み手続 size

size(配列変数名)は配列の大きさを求めます。 配列の大きさは含まれるすべての要素の数です。

[ array-intrinsics3.f90 ] - 配列の組込み手続:size
program array_intrinsics3
  implicit none
  integer a(2), b(2,3), c(2,3,4)
  print *, "size(a(2)) is =", size(a)
  print *, "size(b(2,3)) is =", size(b)
  print *, "size(c(2,3,4)) is =", size(c)
end program array_intrinsics3

出力例:
 size(a(2)) is = 2
 size(b(2,3)) is = 6
 size(c(2,3,4)) is = 24

12.8 部分配列

Fortranには配列の一部分を指定する強力な機能があります。 これにより、例えば2次元配列のある列だけ、あるいはある部分だけへの アクセスが容易に行えます。 配列の一部分を指定するには、各次元の添字を以下の書式で指定します。
  [下限]:[上限][:きざみ]

(補足) 下限を省略した場合は配列の下限が設定される
(補足) 上限を省略した場合は配列の上限が設定される
(補足) きざみを省略した場合は1が設定される

上限、下限のより正確な表現は以下のようになります。
  [初期値]:[終了値][:きざみ]
※a(5:1:-1)のように初期値>終了値といった記述も可能です

以下にinteger配列 a(5) の場合の例を示します。



[ array-section1d.f90 ] - 部分配列(1次元)のサンプル

program array_section1d
  implicit none
  integer a(5)
  a = 0         ! aのすべての成分をゼロにする
  a(1:3) = 99   ! a(1)からa(3)までの3個の成分を99を設定する
  print *, a
  a(:) = 88     ! a(1)からa(5)までの5個の成分に88を設定する
  print *, a
  a(:2) = 77    ! a(1)からa(2)までの2個の成分77を設定する
  print *, a
  a(4:) = 66    ! a(4)からa(5)までの2個の成分に66を設定する
  print *, a
  a(1:5:2) =55  ! a(1), a(3), a(5)に55を設定する
  print *, a
end program array_section1d

出力例:
 99 99 99 0 0
 88 88 88 88 88
 77 77 88 88 88
 77 77 88 66 66
 55 77 55 66 55

以下に2次元配列(integer b(3,4))の場合の例を示します。



[ array-section2d.f90 ] - 部分配列(2次元)のサンプル

program array_section2d
  implicit none
  integer b(3,4), i
  b = 0            ! bのすべての成分を0にする
  b(3,:) = 30      ! bの3行目すべての成分 (b(3,1), b(3,2), b(3,3), b(3,4))に30を設定する
  b(1:2,3:4) = 10  ! b(1,3), b(2,3), b(1,4), b(2,4)に10を設定する
  do i = 1, 3
    print *, b(i,:)
  end do
end program array_section2d

出力例:
 0 0 10 10
 0 0 10 10
 30 30 30 30

この機能を使うと、以下のように配列内の成分を反転させる事も可能です。

[ array-reverse.f90 ] - 部分配列を利用した配列内成分の反転

program array_reverse
  implicit none
  integer a(3)
  a(1) = 10
  a(2) = 20
  a(3) = 30
  a = a(3:1:-1)
  print *, a
end program array_reverse

出力例:
 30 20 10

12.8.1 部分配列の補足説明

[下限]:[上限][:きざみ]の指定方法により部分配列の指定を行った場合、 配列の次元(rank)は変わりません。配列の次元はスカラ添字指定を 行った場合毎に一つずつ低くなります。

例えば integer,dimension(3,4)::a の2次元配列の場合に a(1:1,3:3) と指定した場合、返される配列の形状はinteger,dimension(1,1) となり スカラ値にはなりません。スカラ値がほしい場合には通常の添字(スカラ)で a(1,3) というように指定を行います。

同様に例えば real,dimension(3,4)::b の2次元配列の場合に b(1,3:3)とすると、返される値は次元が1つ下がった1次元配列 (real,dimension(1))となります。これは b(1,3:3)の指定で 最初の添字がスカラであることから次元が一つ下がるためです。 この場合に例えばb(1,3)のように、添字を2つともスカラ添字に した場合には、それぞれのスカラ添字毎に次元が1つずつ下がりますので 次元が0、つまりスカラが返されることになります。

12.9 ★ 練習課題:行列の行毎の合計を表示する

図のような4x3の行列(2次元配列)を用意し、 そのそれぞれ行の各成分の値とそれらの合計を出力するプログラムを作成して下さい。 (部分配列の機能を用いると便利です)

処理手順例

  1. 配列を宣言して初期化する
     例)integer :: a(4,3) = reshape( (/1,2,3,4,5,6,7,8,9,10,11,12/), (/4,3/) )
  2. doループで行毎の処理を行う
     例)do i=1,4 ! 1行目から4行目
  3. 各行に含まれる値とそれらの合計を出力する(print文、部分配列を利用すると便利、合計はsum組込み関数にて行うと良い)
     例)print *, a(i,:), sum(a(i,:))
実行例:
 row 1 contains 1 5 9 and the sum is 15
 row 2 contains 2 6 10 and the sum is 18
 row 3 contains 3 7 11 and the sum is 21
 row 4 contains 4 8 12 and the sum is 24
[ kadai-array-section.f90 ] - 行列の行毎の合計を表示するプログラム例

12.10 配列を引数で渡す方法

配列を引数で渡すには、配列名をそのまま指定します。 受け取る側では以下のようにコロンを用いた記述を行い、どのような大きさであっても 受け取れる用に記述します。その際に受け取る側の関数もしくはサブルーチン内で 配列の大きさを調べるには、組込み関数uboundを利用します。

[ array-argument.f90 ] - 配列を引数で渡す方法のサンプル
program array_argument
  implicit none
  integer i
  real :: a(10)
  do i = 1, 10
    a(i) = i
  end do
  print *, "mysum =", mysum(a)   ! ここで配列 a を引数として渡す
contains
  real function mysum(x)
    real,intent(in) :: x(:)    ! コロンを記述し、どの大きさでも受け取れるようにする
    integer i
    mysum = 0.0
    do i = 1, ubound(x,1)
      mysum = mysum + x(i)
    end do
  end function mysum
end program array_argument

出力例:
 mysum =  55.0000000

多次元配列も同様にコロンを用いた記述が可能ですが、次元数分のコロンをカンマで区切って指定します。

  real function mysum2d(x)
    real,intent(in) :: x(:,:)   ! 2次元の配列
  ...

  real function mysum3d(x)
    real,intent(in) :: x(:,:,:) ! 3次元の配列

12.11 配列の動的割付け

プログラム作成時に配列の大きさがわからない場合など、プログラムの実行時に 配列の領域を確保したい場合があります。このような場合には配列の動的割付けを行います。 動的割付けを行うには、まず動的割付けを行いたい配列の宣言時にallocatable属性を指定し、 更に配列の大きさの指定にはコロンを指定します。実際に配列領域を割付ける際には、 allocate文を用います。領域の解放は自動的に行われますが、もしも明示的に 領域を解放したい場合にはdeallocate文が利用可能です。以下に例を示します。

[ array-alloc.f90 ] - 配列領域動的割付けのサンプル
program array_alloc
  implicit none
  integer n, i
  integer,allocatable,dimension(:) :: a
  print *, "Enter number of data:"
  read *, n
  allocate( a(n) )
  do i = 1, n
    print *, "Enter item(", i, "):"
    read *, a(i)
  end do
  print *, "Total=", sum(a)
  deallocate(a)     ! 特に指定しなくても自動的に領域が解放されます、このように指定することも可能
end program array_alloc

実行例:
 Enter number of data:
3
 Enter item( 1 ):
10
 Enter item( 2 ):
20
 Enter item( 3 ):
30
 Total= 60

12.12 配列を返す関数の作成方法

関数から配列を返したい場合には、関数名を配列として宣言します。 この場合、配列の大きさがあらかじめわかっている場合には以下のように 記述可能です。

[ array-res1.f90 ] - 関数から配列を返すサンプル - 配列の大きさがあらかじめわかっている場合
program array_res1
  implicit none
  real a(3)
  a = myfunc()
  print *, a
contains
  function myfunc()
    real myfunc(3)     ! ここで結果を配列として宣言
    myfunc(1) = 10
    myfunc(2) = 20
    myfunc(3) = 30
  end function myfunc
end program array_res1

出力例:
  10.0000000  20.0000000  30.0000000

多くの場合、固定の大きさではなく、引数として渡ってくる値を元に配列の大きさを 決定する必要があります。このような場合には以下のように指定可能です。

[ array-res2.f90 ] - 関数から配列を返すサンプル - 配列の大きさがあらかじめわかっていない場合

program array_res2
  implicit none
  real a(3)
  a = myfunc(3)
  print *, a
contains
  function myfunc(n)
    integer,intent(in) :: n
    real myfunc(n)     ! ここで入力引数 n の値を用いて、結果を配列として宣言
    myfunc = 99
  end function myfunc
end program array_res2

出力例:
  99.0000000  99.0000000  99.0000000

また別のケースでは、入力された配列と同じ大きさの配列を出力したいような 場合もあります。その場合には大きさの指定箇所で size 組込み関数を 利用できます。例えば以下の例は、入力された配列のすべての成分に1を足したもの を返す関数です。

[ array-res3.f90 ] - 関数から配列を返すサンプル - 配列の大きさを入力配列引数から求めたい場合

program array_res3
  implicit none
  real v(3), r(3)
  v = 0   ! 配列 v のすべての成分をゼロにする
  r = myfunc(v)
  print *, "original:", v
  print *, "result  :", r
contains
  function myfunc(x)
    real,intent(in),dimension(:) :: x
    real myfunc(size(x))  ! size(x) の値を用いて、結果を配列として宣言
    myfunc = x + 1.0
  end function
end program array_res3

出力例:
 original:   0.0000000   0.0000000   0.0000000
 result  :   1.0000000   1.0000000   1.0000000

12.13 ★ 練習課題:血液型の分布を調べる

調べる対象人数を入力後、それぞれの血液型を入力し、血液型毎に 何人いるかを表示するプログラムを作成して下さい。 この際にそれぞれの血液型は以下のように数字で表すものとします。

血液型 数字
A型 1
B型 2
O型 3
AB型 4

また、血液型毎に何人いるかを数える処理は関数として実装して下さい。

処理手順例

  1. 変数を宣言する
     例)integer,allocatable :: btype(:)
  2. 人数を入力してほしい旨を画面に出力する(print文)
  3. 人数をキーボードから入力する(read文)
  4. 人数分の配列領域を割付ける
     例)allocate(btype(n))
  5. doループで以下の2つの処理を行う
  6. 血液型(1-4)を入力してほしい旨を画面に出力する(print文)
  7. 血液をキーボードから入力して配列に格納する(read文)
  8. A型の人数を関数にカウントさせて表示する
     例)print *, "Type A :", count_type(btype,1)
  9. B型の人数を関数にカウントさせて表示する
     例)print *, "Type B :", count_type(btype,2)
  10. O型の人数を関数にカウントさせて表示する
     例)print *, "Type O :", count_type(btype,3)
  11. AB型の人数を関数にカウントさせて表示する
     例)print *, "Type AB :", count_type(btype,4)
実行例:
 Enter number of people:
7
 Enter Blood Type for # 1
1
 Enter Blood Type for # 2
2
 Enter Blood Type for # 3
3
 Enter Blood Type for # 4
4
 Enter Blood Type for # 5
1
 Enter Blood Type for # 6
1
 Enter Blood Type for # 7
2
 Type A : 3
 Type B : 2
 Type O : 1
 Type AB: 1
[ kadai-blood-type.f90 ] - 血液型の分布を調べるプログラム例


ナビゲーション:前へ   上へ   次へ

Results matter. Trust NAG.
Privacy Policy | Trademarks