Fortran 入門

前へ   上へ   次へ

18 知識として必要な過去の Fortran

ここでは新しく書くプログラムでは必要ないものの、既に世の中に存在する多くの Fortran プログラム資産を理解する上で必要な Fortran の機能について説明をします。

18.1 固定形式

固定形式はパンチカードによってプログラムを入力していた時代の産物ですので、新しくプログラムを作成する場合の使用は推奨されません。 しかしながら過去に作成されたプログラムを理解したり、あるいはメンテナンスしていく上である程度の知識が必要とされますので、以下の要点を説明します。
  • 1 行あたり 72 カラムまでの制限がある。
  • 1 カラム目から 5 カラム目には文番号を記述できる。 特に 1 カラム目に 'C' や '*' などの数字以外が記述されている場合には、その行がコメント行として解釈される。
  • 6 カラム目は通常は空白にする。空白でない場合(何か文字を指定した場合には)前の行からの継続行とみなされる。
  • 7-72 カラム目にプログラム文を記述する。
  • 文字定数表現以外の空白は意味がないので、例えば WRITE も W R ITE も同様に扱われる。 同様に INTEGERABC としても INTEGER ABC と同じ意味となる。


[ fixed.f ] - 固定形式のサンプル
      PROGRAM MAIN
C  以下の行のようにキーワード(CHARACTER)の後の空白はいらない
      CHARACTERMYNAME*20
C この行はコメント
      WRITE (*,*) "Please enter your name:"
C  下記は 2 行にわたって記述する例(継続行の例)
      READ (*,*)
     &MYNAME
C  下記は空白が読み飛ばされる例
      WR I TE (*,*) "Name Entered was:", MYNAME
      END

出力例:
 Please enter your name:
Taro
 Name Entered was:Taro

18.2 共有(COMMON)ブロック

共有ブロックはプログラム単位間でデータのやり取りをする場合などに利用可能な機能ですが、探し出しにくいバグの原因となるので、その利用は推奨されていませんが過去のプログラムに多くみられますので、ここで概要を説明します。

18.2.1 共有ブロックの利用方法

共有ブロックは以下のように COMMON 文を用いて利用します。
  COMMON [/ブロック名/]変数並び

同じブロック名は同じデータ領域を意味します。 また、ブロック名を指定しない場合は特別な共有ブロックで無名共有ブロックと呼ばれます。

共有ブロックは例えば以下のように利用しデータの共有が可能となります。

[ common.f90 ] - COMMON ブロックのサンプル

subroutine mysub
  common /mydata/x,y,z   ! mydata という共有ブロックを指定
  x = 99
  y = y + 1.0
end subroutine

program common
  common /mydata/x,y,z   ! ここでも mydata という共有ブロックを指定
  x = 5.0
  y = 10.0
  z = 15.0
  print *, "x =", x, "y =", y, "z =", z
  call mysub
  print *, "x =", x, "y =", y, "z =", z
end program common

出力例:
 x =   5.0000000 y =  10.0000000 z =  15.0000000
 x =  99.0000000 y =  11.0000000 z =  15.0000000

18.2.2 共有ブロックの初期化方法

共有ブロック(無名共有ブロックを除く)は data 文により初期化することができます。 その場合に初期値設定プログラム単位内にその記述を行わなければなりません。 初期値設定プログラム単位は以下のように書式の通り記述します。
block data [初期値設定プログラム名]
  [単純宣言文]...
end [block data [初期値設定プログラム名]]

例)
block data
  common /mydata/ i,j
  data i,j/10,20/
end block data

以下に共有ブロックの初期化を行うサンプルを示します。

[ common-init.f90 ] - COMMON ブロックの初期化を行うサンプル

program common_init
  implicit none
  integer a(4), b
  common /mydata/ a,b
  print *, "a =", a, "b =", b
  call mysub
  print *, "a =", a, "b =", b
end program

subroutine mysub
  implicit none
  integer a(4), b
  common /mydata/ a,b
  a = a + b
end subroutine

block data
  implicit none
  integer a(4), b
  common /mydata/ a,b
  data a/1,2,3,4/, b/100/
end block data

出力例:
 a = 1 2 3 4 b = 100
 a = 101 102 103 104 b = 100

18.3 暗黙の型宣言

Fortran では変数の宣言が必須ではありません。 プログラムの保守性の観点から、本テキストでは常に変数の宣言を必須とする implicit none の利用を強く推奨しています。 しかしながら世の中に存在する多くの Fortran プログラムは、変数の宣言を行わずに変数を利用するやり方(implicit none を指定しない記述方法)が使われていますので、ここで説明します。

18.3.1 implicit 文を全く使わない暗黙の型宣言ルール

Fortran では implicit 文を指定しない場合に変数名の最初の文字により変数の型が決定されます。 これを暗黙の型宣言と呼びます。

暗黙の型宣言は以下のルールに基づきます。
a-h, o-z で始まる変数は基本実数型となる
i-n で始まる変数は基本整数型となる
以下に例を示します。

[ implicit.f90 ] - 暗黙の型宣言を用いるサンプル

program implicit
  a = 100   ! 基本実数型
  i = 100   ! 基本整数型
  print *, "a =", a
  print *, "i =", i
end program implicit

出力例:
 a =   1.0000000E+02
 i = 100

18.3.2 implicit 文によって任意の型をマッピングする

過去のプログラムには以下のように implicit 文を用いて、任意の英文字ではじまる変数の型を決定する方法も多く見うけられます。
  implicit 型 (英字範囲並び), [,型 (英字範囲並び)]...

すべての実数変数を倍精度として扱いたいような場合に例えば以下のような記述が可能です。

  implicit real*8(a-h,o-z)   ! a から h、o から z ではじまる変数の型を real*8 として扱う

18.4 DO ループの古い書き方

過去のプログラムにおいて DO ループは文番号を指定する以下のような書式が最も一般的でした。 (新しいプログラムでは文番号を伴わない DO ~ END DO が推奨されています。)
DO 文番号 変数=初期値, 最終値 [,刻み幅]
  繰り返したい処理
文番号 CONTINUE

ここで continue 文は特に何もしません。 (「場所取り」の役割を果たしています。)

例)
    do 20 i=1, n
      do 10 j=2, m
        a(i,j) = i*m
 10   continue
 20 continue

2重ループを1つの continue で共有することも可能で、以下のような記述もよく見られます。
以下は上記と同じ動作する例です。
    do 10 i=1, n
      do 10 j=2, m
        a(i,j) = i*m
 10 continue

18.5 GOTO 文

過去のプログラムでは GOTO 文が多用されている傾向にあります。 GOTO 文は文番号が指定する場所へ処理を移行させる記述方法です。
GOTO 文番号
もしくは
GO TO 文番号

例)
     if ( stat .ne. 0) goto 10
     ...
  10 print *

GOTO には実はもう一つの書き方(計算形)があります。 計算形 GOTO は Fortran において廃止事項ですが古いプログラムで見うけられる記述方法ですのでここで説明します。 計算形 GOTO は与えられた式(整数)の値により、その位置に記述された文番号へジャンプします。

GOTO (文番号並び) [,] 整数式

例)
   index = 3
   go to (10,20,30) index - 1 ! 2番目の文番号 20 へジャンプ
   stop
10 print *, 'LABEL:10'
   stop
20 print *, 'LABEL:20'
   stop
30 print *, 'LABEL:30'
   stop
   end

18.6 大きさ引継ぎ配列

サブルーチンや関数で配列を引数として受け取る際に仮引数の最終次元の大きさ指定を *(アスタリスク)とする記述方法があります。 これは配列の寸法を実引数から引き継ぐことを意味する記述で大きさ引継ぎ配列と呼ばれます。 新しく書くプログラムではより安全で便利な形状引継ぎ配列を利用することを推奨します。

以下に大きさ引継ぎ配列の例を示します。

program assumed_size
  implicit none
  integer :: a(2) = (/1,2/)
  integer :: b(3,2) = reshape( (/1,2,3,4,5,6/), (/3,2/) )
  call mysub(a,b)
contains
  subroutine mysub(a,b)
    integer a(*), b(3,*)
    ! このサブルーチン内で a の大きさや b の大きさを知るすべが無い
    ! 例えば print *, a や b(1,:) 等とはできない
    print *, a(1), a(2)      ! このように指定することは可能
    print *, b(1,1), b(1,2)  ! このように指定することは可能
  end subroutine
end program

出力例:
 1 2
 1 4

以下に比較の対象として新しい書き方である形状引継ぎ配列の例を示します。

program assumed_shape
  implicit none
  integer :: a(2) = (/1,2/)
  integer :: b(3,2) = reshape( (/1,2,3,4,5,6/), (/3,2/) )
  call mysub(a,b)
contains
  subroutine mysub(a,b)
    integer a(:), b(:,:)     ! このようにコロンを記述する事で形状引継ぎとなる
    print *, a               ! 配列の形状が配列と共に渡ってきているのでこのような記述もOK
    print *, b(1,:)          ! 部分配列もOK
    print *, "aの各次元の大きさ =", shape(a)   ! 配列の形状がちゃんと取得できる
    print *, "bの各次元の大きさ =", shape(b)   ! 配列の形状がちゃんと取得できる
  end subroutine
end program

出力例:
 1 2
 1 4
 aの各次元の大きさ = 2
 bの各次元の大きさ = 3 2

18.7 算術 IF 文

古い書き方に算術 IF というものがあります。 この書き方は Fortran では廃止予定機能ですが古いプログラムには見かけられる書き方です。

算術 IF 文は、与えた式が 0 より小さい場合に最初の文番号へ、0 の場合は 2 番目の文番号へ、0 よりも大きい場合には 3 番目の文番号にジャンプします。

if ( 式 ) 文番号 1, 文番号 2, 文番号 3

例)
i = -1
if (i) 100, 200, 300   ! 100 へジャンプします

18.8 入出力時、及び DATA 文においての DO 形反復

過去のプログラムでは入出力時や DATA 文において配列成分を順番に指定したい場合に DO 形反復が多用されています。 この書き方は特に悪いものではありませんが、新しいプログラムにおいてはよりわかりやすい配列全体を指定する方法や部分配列が推奨されています。

以下に DO 形反復を用いて配列の入出力を行っている例を示します。

  integer,dimension(3,2) :: a, b
  ...
  write (10,*) ((a(i,j),i=1,3),j=1,2)    ! DO 形反復を用いて配列を全体書き出す例
  write (10,*) a                         ! 配列全体を指定。現在はこの書き方が推奨されています
  ...
  read (11,*) ((b(i,j),i=1,3),j=1,2)    ! DO 形反復を用いて配列(全体)を読み込む例
  read (11,*) b                         ! 配列全体を指定。現在はこの書き方が推奨されています

18.9 PARAMETER 文と DIMENSION 文

PARAMETER 文と DIMENSION 文は過去のプログラムに多く利用されていますが、新しいプログラムでは推奨されない書き方です。 (代わりに変数宣言時に属性としてこれらを指定することが推奨されています。)

18.9.1 PARAMETER 文

PARAMETER 文は名前付き定数を指定する際に使われますが以下のような書式を持ちます。
parameter( 定数名=初期値式 [,定数名=初期値式]... )

例)
【古いやり方】 - PARAMETER 文による指定方法
integer Red, Green, Blue              ! この宣言が無い場合は暗黙の型宣言に基づく型となる
PARAMETER( Red=1, Green=2, Blue=3 )

【より良いやり方】 - PARAMETER 属性を指定する方法
integer,parameter :: Red=1, Green=2, Blue=3

18.9.2 DIMENSION 文

DIMENSION 文は配列の宣言を行うもので、古いプログラムでよく見られる書き方です。 以下のような書式を持ちます。
dimension [::] 配列名(配列指定) [,配列名(配列指定)]... )

例)
【古いやり方】 - DIMENSION 文による指定
integer a, b              ! この宣言が無い場合は暗黙の型宣言に基づく型となる
dimension a(10), b(4,3)

【より良いやり方(1)】 - 変数の宣言時に指定する
integer a(10), b(4,3)

【より良いやり方(2)】 - 変数の宣言時に dimension 属性を指定する
integer,dimension(10) :: a
integer,dimension(4,3) :: b

18.10 EXTERNAL 文

古いプログラムでは EXTERNAL 文が外部関数や外部サブルーチンを利用したい場合や引数として関数を渡したい場合などに利用されています。

External 文は以下の形式を持ちます。

external 外部名並び

external 文は実行部より前の宣言部(use 文や implicit 文がある場合はそれらより後ろ)に記述します。

external 文により宣言された外部手続名と組込み手続等の名前が重なる場合にはその名前を持つ組込み手続等は使用不可となり外部手続が優先されます。

EXTERNAL 文では手続が外部手続である事を宣言するに過ぎず引数の型等の情報は含まれません。 そのため新しく作成するプログラムではEXTERNAL文の代わりに引用仕様(INTERFACE)を用いることが推奨されています。 引用仕様を用いることにより引数の型も含めた形での宣言が行えます。

尚、手続は EXTERNAL 文で宣言することもしくは引用仕様を与えることが許されていますが、両方を指定することは許されていません。

以下の外部サブルーチンを利用したい場合の例

! 単精度実数 a + b を表示する
subroutine add_and_print(a, b)
    real a, b
    print *, a + b
end subroutine add_and_print

【External 文を利用する例 - 誤った引数の型であってもエラーとならない】
program external_example
  implicit none
  external add_and_print
  integer :: a=1, b=2
  call add_and_print(a,b)
end program external_example

出力例:
   4.2038954E-45    ← 引数の型の整合性が取れていなかったのでおかしな結果となってしまった

【引用仕様を利用する例 - より良いやり方=誤った引数の型でエラーを検出してくれる】
program interface_example
  implicit none
  interface
    subroutine add_and_print(a,b)
      real a, b
    end subroutine
  end interface
  integer :: a=1, b=2
  call add_and_print(a,b)  ! エラー 「誤ったデータ型 INTEGER(正しくは REAL)が指定されました」等となる
end program interface_example

18.11 文関数

文関数は同じプログラム単位内で同じ計算を複数箇所で行いたいような場合に利用されます。 例えば y = 2 + 5x + 6x2 を複数箇所で計算させたいような場合以下のように記述します。
  myexpr(x) = 2. + 5.*x + 6.*x*x
  ...
  print *, myexpr(1.)     ! 2+5*1+6*1*1 = 13
  print *, myexpr(2.)     ! 2+5*2+6*2*2 = 22

文関数は新しいプログラムでは推奨されません。 新しいプログラムでは内部関数の利用が推奨されています。 文関数を利用するプログラムコードと内部関数を利用するプログラムコードを示します。

【文関数を用いる例 - 現在は推奨されません】
program stmt_func
  implicit none
  real x, myexpr
  myexpr(x) = 2. + 5.*x + 6.*x*x    ! 文関数の定義
  print *, myexpr(1.), myexpr(2.)   ! 文関数の利用
end program stmt_func


【内部関数を用いる例 - 推奨される書き方】
program internal_func
  implicit none
  print *, myexpr(1.), myexpr(2.)   ! 内部関数の利用
contains
  function myexpr(x)                ! 内部関数の定義
    real x, myexpr
    myexpr = 2. + 5.*x + 6.*x*x
  end function myexpr
end program internal_func


出力例:
  13.0000000  36.0000000

18.12 その他の廃止事項等

上記以外で過去に許されていた形式で現在は廃止もしくは非推奨となっている機能の主なものを以下に示します。
(※ あらたに書くコードには必要ありません。)
  • ASSIGN 文
  • 割当て形 GO TO 文
  • 割当て形 FORMAT 文
  • ホレリス(H 形編集記述子)
  • PAUSE 文
  • 実数型 DO 制御変数
  • IF ブロック外から ENDIF へのジャンプ
  • EQUIVALENCE 文
  • ENTRY 文



前へ   上へ   次へ
大学生協限定キャンペーン情報
ご案内
関連情報
© 日本ニューメリカルアルゴリズムズグループ株式会社 2025
Privacy Policy  /  Trademarks