はじめての Fortran プログラミング

この Notebook は,

のプログラミング部分に相当する内容を Fortran を使って書いてみたものです。

章番号はオリジナルの従来テキストに対応するように8章からはじめていますので,従来テキストを使用して授業を展開してきた教員にも対応がわかりやすいかと思います。

また,テキストや授業と関係なく,初めて Jupyter Notebook で Fortran を利用する学生や教職員にとっても参考になれば幸いです。(はたしてどれだけの需要があるかはわかりませんが... )

では,はじめましょう。

まずは,以下のような掛け算の答えを出力するプログラムを実行してみます。

  • Fortran のプログラムは,1行目のように program ... で始まり,...
  • 最終行のように end program で終わります。
  • 2行目では,数値 2.5 を変数 boku に代入しています。人一倍どころか,人より 2.5 倍の思いがあることを示しています。
  • 3行目では,数値 0 を変数 kanojo に代入しています。
  • 4行目では,変数 boku に代入された数値と,変数 kanojo に代入された数値をかけた答えを,変数 love に代入しています。
  • 5行目では,変数 love の値を出力しています。

本稿のように,Jupyter Notebook を使う場合は,

  1. セル(In [ ]: で始まる四角い部分)にプログラム文を入力し,
  2. Enter キー(Return キー)で改行。
  3. ブログラム文を入力し終わったら,Shift キーを押しながら Enter キー(Return キー)で実行します。
    上部のツールバーの「▶︎ Run」をクリックしても実行します。
In [1]:
program ai
  boku = 2.5
  kanojo = 0
  love = boku * kanojo
  print *, love
end program
           0

エラーなく実行されたが,結果が 0 というのは,あまりに寂しい。

以下のように少し変更して実行すると...

In [2]:
program ai2
  watashi = 2.5
  kareshi = 0.4
  love = watashi * kareshi
  print *, love
end program
           0

おかしい... 2.5 に 0.4 をかければ 1.0 になるはずだが...

大事な約束事:暗黙の型宣言

歴史的慣習により,Fortran では,黙っていれば i, j, k, l, m, n で始まる変数は 整数,それ以外のアルファベットで始まる変数は 実数 として扱われる。

上記の例題 program ai2 では,kareshi = 0.4 は実は kareshi = 0 のこと。

このような思いもかけないトラブル(アイにトラブルはつきもの?)を防ぐ意味も兼ねて,最近の教科書では,この暗黙の型宣言を無効にするように,プログラム文の冒頭に

implicit none

と書くことを推奨している。確かにその通りですが,ここではプログラム文をあまり長くしないという観点から,はじめのうちは暗黙の型宣言に従う書き方で解説する。

上記の program ai2 は,以下のように変数を書くと良いことになる。(実数変数の先頭に r をつけて real(実数)であることを示してみましたが,現代的でないですかねぇ。)

In [3]:
program ai3
  watashi = 2.5
  rkareshi = 0.4
  rlove = watashi * rkareshi
  print *, rlove
end program
   1.00000000    

現代的視点から推奨されている書き方を以下に例示します。

  • Fortran プログラムは,1行目のように program ... から始まります。
  • 2行目から4行目までは,! からはじまるコメント行です。コメント行はそれを読む人間のために注釈であり,プログラムの実行時には無視されます。
  • 5行目で,暗黙の型宣言を無効にします。implicit none と宣言したら,以降はプログラム文内で使用する変数は,全て以下の例のように宣言する必要があります。
  • 6行目で,これから使う変数の型を real(基本実数型,単精度実数とも)として宣言します。
  • 7行目では,数値 2.5 を変数 watashi に代入しています。人一倍どころか,人より 2.5 倍の思いがあることを示しています。
  • 8行目では,数値 0.4 を変数 kareshi に代入しています。
  • 9行目では,変数 watashi に代入された数値と,変数 kareshi に代入された数値をかけた答えを,変数 love に代入しています。
  • 10行目では,変数 love の値を出力しています。
  • Fortran プログラムは,最終行のように end program ... で終わります。
In [4]:
program ai4
! このように,! から始まる行はコメント行。実行に影響しません。
! 暗黙の型宣言は無効にして,
! プログラムで使う変数は全てあらかじめ宣言しておきます。
  implicit none
  real :: watashi, kareshi, love
  watashi = 2.5
  kareshi = 0.4
  love = watashi * kareshi
  print *, love
end program ai4
   1.00000000    

はじめてのプログラミング

エンゲル係数とは,家計の消費支出に占める飲食費(食料費,食費)の割合(パーセント単位)です。

消費支出 100,000円,飲食費 40,000円の人のエンゲル係数を求めるプログラム例。

本稿のように Jupyter Notebook 環境では,セル内で Enter キー(Return キー)を押すと改行され,最後の行を書いたあとに,Shift キーを押しながら Enter キー(または Return キー)を押して実行します。

最低限,以下の2行で動きます(最終行の end は必須)。 1行目の割り算の分子を 100.0 とした理由はわかりますか? 

In [5]:
print *, 40000*100.0/100000
end
   40.0000000    

ここからは,単に電卓的に使うだけではなく,応用・発展も見据えて以下のようなプログラム文を作成して実行することにします。

以下の例では,

  • プログラム文は大文字で書いても小文字で書いてもよい。(大文字と小文字は区別されない。)
  • 最初と最後,program ではじめて end program で終わるのはお約束。
  • 2行目で変数 s1 に消費支出である 100000(円)という数値を代入し,
  • 3行目で変数 s2 に飲食費である 40000(円)という数値を代入し,
  • 4行目でエンゲル係数を計算して,変数 x1 に代入し,
  • 5行目でエンゲル係数の値 x1 を表示させています。
In [6]:
program engel
  s1 = 100000
  s2 = 40000
  x1 = s2*100.0/s1
  print *, x1
end program
   40.0000000    

エラーとデバグ

意に反して,プログラムがうまく動作しない場合があります。たとえば,上記の 3行目を以下のように書いた場合。

掛け算を示す演算子は *,割り算を示す演算子は / であるところを誤って,×÷ と書くと,これらの記号は Python では演算子として定義されていないのでエラーとなります。このようなエラーは文法エラー(SyntaxError) と呼ばれ,Jupyter Notebook では,以下のように誤りやその箇所を指摘してくれます。

In [7]:
program engel
  s1 = 100000
  s2 = 40000
  x1 = s2 × 100 ÷ s1
  print *, x1
end program
/var/folders/pt/2hm5g18j5r13rhf__bx8k6km0000gn/T/tmpv_nj_av2.f90:4:12:

    4 |   x1 = s2 × 100 ÷ s1
      |           1
Error: Invalid character 0xC3 at (1)
[Fortran kernel] gfortran exited with code 1, the executable will not be executed

別の種類の誤りもあります。たとえば,4行目の「100」を「1000」と入力した場合...

In [8]:
program engel
  s1 = 100000
  s2 = 40000
  x1 = s2*1000/s1
  print *, x1
end program
   400.000000    

上記の例では Error (文法エラー)の警告が出ませんが,答えは明らかに変です。(エンゲル係数の最大値は100だから。)

このような場合はコンピュータにとって誤りではなく,指示通りに計算し,その結果を表示します。 このようなプログラム作成者の不注意や勘違いによって生じるエラーには注意が必要です。

プログラムが正しく動作しない場合は,誤りの箇所を探し,修正して再度動作を確認します。正しく動作するようになるまで,プログラムを修正し,実行(Jupyter Notebook 環境では Shift + Enter)します。この作業をデバグといいます。

なお,Jupyter Notebook の Fortran kernel では,バグのためか,1回 Shift キーを押しながら Enter キーを押しても実行結果が表示されない場合があります。そんなときは,同じセルを選択し,もう一度,Shift + Enter してみてください。

データの型と変数

上記の例の 1行目では,s1 という名前の変数に 100000 という数値を代入しています。変数名はアルファベットで始まる英数字列にします。大文字と小文字は区別されません。

変数 ss に文字列を代入する場合は,以下のように " で囲みます。

In [9]:
character (12) :: ss
ss = "engel keisu "
print *, ss
end
 engel keisu 

式と演算

掛け算を示す演算子は *,割り算を示す演算子は /。足し算は +,引き算は - です。

// は文字列の連結演算に使います。以下の例を参照。

In [11]:
character (6) :: x0, y0
x0 = "engel "
y0 = "keisu "
print *, x0 // y0
end
 engel keisu 

文字列と数値を直接連結することはできません。

In [12]:
s1 = 100000
s2 = 40000
x1 = s2*100/s1
print *, "エンゲル係数は " // x1
end
[Fortran kernel] gfortran exited with code 1, the executable will not be executed

そんなときは,数値を文字列に変換する関数を使いたいところですが,Fortran にはないようです。

print のところは,文字列にして連結しなくても,以下のようにも書くことができます。 print 文は,カンマ (,) で区切ることによって複数の項目(変数)を表示することができます。

In [13]:
s1 = 100000
s2 = 40000
x1 = s2*100/s1
print *, "エンゲル係数は ", x1, "です。"
end
 エンゲル係数は    40.0000000     です。

主な演算子

加減乗除の演算子 +, -, *, /

優先的に計算したい箇所は丸括弧 ( )で グループ化します。

In [14]:
print *, 2 + (50 - 5 * 6)/4
end
           7

除算 / の例を以下に示します。

  • 1行目では,整数を整数で割るので,小数分を切り捨てた整数値を返します。

  • 2行目では,17. と最後に小数点をつけたので,単精度実数型(real)の値を返します。この例では,小数点以下6桁くらいまでの精度です。

  • 3行目では,17.D0 と指数部の表示に D を使うことで倍精度実数型(double precision)の値を返します。この例では,小数点以下14桁くらいまでの精度です。

In [15]:
print *, 17 / 3
print *, 17./ 3
print *, 17.D0 / 3
end
           5
   5.66666651    
   5.6666666666666670     

べき乗(累乗)は ** です。

In [16]:
print *, 2**4
end
          16

べき乗に限らず,あまりにも桁数が多くなる計算すると,基本整数型(integer)の範囲を超えてしまい,おかしな値を出力することがあるので注意。以下の例を参照。

In [18]:
print *, 2**100
end
 -2147483648

対話型プログラム

上記の例では,プログラムの中で消費支出や飲食費を決めていましたが,今度はプログラムを実行する人が入力できるようにしてみます。

以下のプログラム例では,read *, s1 でキーボードからの入力値を変数 s1 に入れます。

In [ ]:
program sec8_5
    print *, "消費支出? "
    read *, s1
    print *, "飲食費? "
    read *, s2
    x1 = s2*100/s1
    print *, "エンゲル係数は", x1
end program

どうも,Jupyter Notebook 環境では,Fortran の read 文が入っていると,うまくいかないようです。

ここでは,ターミナルでコンパイル・実行した例を以下に示します。

あらかじめ,以下のようなソース(ファイル名 sec8_5.f90 のテキストファイル)を用意します。

$ cat sec8_5.f90 
program sec8_5
    print *, "消費支出? "
    read *, s1
    print *, "飲食費? "
    read *, s2
    x1 = s2*100/s1
    print *, "エンゲル係数は", x1
end program

次に,コンパイルします。

$ gfortran -o sec8_5 sec8_5.f90

エラーなくコンパイルできたら,実行します。

$ ./sec8_5
 消費支出? 
100000
 飲食費? 
40000
 エンゲル係数は   40.0000000

関数の定義と呼び出し

(これまでの例では1回しか計算していませんが)よく使う計算を「関数」として定義する例です。

以下の例では,1行目から3行目でエンゲル係数を計算する関数 calc() を定義し,8行目でその関数を呼び出しています。

In [19]:
function calc(s1, s2)
    calc = s2 * 100 / s1
end

program engel
    s1 = 100000
    s2 = 40000
    x1 = calc(s1, s2)
    print *, "エンゲル係数は", x1
end program
 エンゲル係数は   40.0000000    

(練習)

消費支出を入力すると,エンゲル係数が 20 ~ 40 となる飲食費を表示するプログラムをつくりなさい。

組み込み関数を使う

関数は上記の例のように自分で定義することもできますが,あらかじめ用意されている「組み込み関数」を使うこともできます。

以下では,組み込み関数の例として,

  • 小数点以下を切り捨てる floor()
  • 小数点以下を四捨五入する nint()

を使用しています。

  • ちなみに,小数点以下を切り上げる関数は ceiling() です。
In [20]:
s1 = 130000
s2 = 53000
x1 = s2 * 100 / s1
print *, x1, floor(x1), nint(x1)
end
   40.7692299              40          41

Fortran で使える組み込み関数

Fortran で使える組み込み関数については,インターネット検索すると,いろいろ見つかります。たとえば,以下の JAMSTEC のサイトが参考になります。

(練習)

小数点以下を切り上げたエンゲル係数の値を表示させるように上記のプログラムを変更しなさい。

条件分岐

条件分岐とは,例えばエンゲル係数の値によって実行文を変えることです。例を示します。

In [ ]:
program sec8_8
  print *, "消費支出? "
  read *, s1
  print *, "飲食費? "
  read *, s2
  x1 = s2*100/s1
  if (x >= 80) then
    print *, "エンゲル係数は", nint(x1), "です。飲食費を使いすぎです。"
  else
    print *, "エンゲル係数は", nint(x1), "です。"
  end if
end program

以下が実行結果です。(read 文があると Jupyter Notebook 環境ではうまく動かないので,ターミナルでコンパイル・実行しています。)

$ gfortran -o sec8_8 sec8_8.f90

$ ./sec8_8
 消費支出?
100000
 飲食費?
85000
 エンゲル係数は          85 です。飲食費を使いすぎです。

$ ./sec8_8
 消費支出?
120000
 飲食費?
70000
 エンゲル係数は          58 です。

条件分岐の if 文の書式は以下のようになっています。

if (条件式1) then
    条件式1 が満たされた場合に実行する文
  else if (条件式2) 
    条件式2 が満たされた場合に実行する文
  else
    それ以外の場合に実行する文
end if

(練習)

上記の条件分岐を,エンゲル係数が

  1. 80 以上の場合には「飲食費を使いすぎです。」
  2. 80 未満 40 以上の場合には「正常です。」
  3. 40 未満の場合には「もっと食べましょう。」

と表示させるように変更しなさい。

繰り返し処理

以下の例では,1行目で代入された消費支出に対してエンゲル係数を計算します。

1回だけ計算して表示するのではなく,2行目の飲食費の初期値(ここでは 40000 円)から,5行目の条件式(ここでは 80000 円以下)が成り立つ場合に,8行目にあるように 5000 円ずつ増やしながらエンゲル係数を表示します。

In [21]:
s1 = 100000
s2 = 40000
print *, "消費支出", int(s1), "円に対するエンゲル係数の値"

do while (s2 <= 80000)
    x1 = s2 * 100 / s1
    print *, "  飲食費", int(s2), "円の場合: ", x1
    s2 = s2 + 5000
end do

end
 消費支出      100000 円に対するエンゲル係数の値
   飲食費       40000 円の場合:    40.0000000    
   飲食費       45000 円の場合:    45.0000000    
   飲食費       50000 円の場合:    50.0000000    
   飲食費       55000 円の場合:    55.0000000    
   飲食費       60000 円の場合:    60.0000000    
   飲食費       65000 円の場合:    65.0000000    
   飲食費       70000 円の場合:    70.0000000    
   飲食費       75000 円の場合:    75.0000000    
   飲食費       80000 円の場合:    80.0000000    

do while 文の書式は以下の通りです。

do while (条件式)
    条件式が成り立つ場合に実行される文
end do
In [22]:
s1 = 100000
print *, "消費支出", int(s1),"円に対するエンゲル係数の値"

do i = 40000, 80000, 5000
    x1 = i * 100.0 / s1
    print *, "  飲食費", i, "円の場合: ", x1
end do

end
 消費支出      100000 円に対するエンゲル係数の値
   飲食費       40000 円の場合:    40.0000000    
   飲食費       45000 円の場合:    45.0000000    
   飲食費       50000 円の場合:    50.0000000    
   飲食費       55000 円の場合:    55.0000000    
   飲食費       60000 円の場合:    60.0000000    
   飲食費       65000 円の場合:    65.0000000    
   飲食費       70000 円の場合:    70.0000000    
   飲食費       75000 円の場合:    75.0000000    
   飲食費       80000 円の場合:    80.0000000    

do 文の書式は以下の通りです。

do ivar = istart, iend, istep
    ivar が istart から iend の間,実行される文
end do

ivar は整数変数で,その初期値は istart。文が実行されるごとに,ivar の値は istep だけ加算され,iend の値になるとループが終了します。

上記の例では,念のため,基本変数型(integer)の i を実数型(real)に変換する組み込み関数 float() を使っています。

(練習)

以下の値を求めるプログラムを作成せよ。

$$\sum_{n = 1}^{10} n^2$$