- Fortranの関数・サブルーチン・ファイル操作|初心者向けに基本から解説
- 関数やサブルーチンを使う理由
- 関数(function)とは
- containsとは
- 引数とは
- resultとは
- intent(in)・intent(out)・intent(inout)
- 円の面積を求める関数
- サブルーチン(subroutine)とは
- サブルーチンへ値を渡す
- サブルーチンから値を返す
- 複数の値を返すサブルーチン
- intent(inout)の例
- 関数とサブルーチンの違い
- ファイル操作とは
- ファイル操作の基本的な流れ
- ファイルへ文字を書き込む
- unit番号とは
- newunitを使う方法
- 数値をファイルへ保存する
- ファイルから値を読み込む
- 複数列のデータを読み込む
- ファイルの最後まで読み込む
- iostatとは
- open文の主な指定
- ファイルへ追記する
- 関数とファイル操作を組み合わせる
- 配列引数の「(:)」とは
- 配列で便利な組み込み関数
- よくある初学者のミス
- ここまででできること
- まとめ
Fortranの関数・サブルーチン・ファイル操作|初心者向けに基本から解説
この記事では、Fortranの基本文法を学んだ人向けに、
関数(function)、
サブルーチン(subroutine)、
そしてファイルの読み書きについて解説します。
プログラムが少し長くなってくると、
すべての処理を1つのprogramの中へ書くよりも、
計算や処理ごとに分けた方が読みやすくなります。
Fortranでは、
「値を計算して1つの結果を返す処理」には関数、
「複数の処理をまとめたり、複数の値を受け渡したりする処理」には
サブルーチンを使うことができます。
また、計算結果を画面に表示するだけでなく、
ファイルへ保存したり、
保存済みのデータを読み込んだりすることもできます。
この記事では、初学者でもコードの意味を追いやすいように、
サンプルコード内の「!」によるコメント(注釈)を多めに記載しています。
関数やサブルーチンを使う理由
短いプログラムであれば、
すべての処理を1つのprogram内へ書いても問題ありません。
しかし、処理が増えてくると、同じ計算を何度も書いたり、
どの部分が何をしているのか分かりにくくなったりします。
例えば、円の面積を毎回次のように計算することもできます。
area = 3.14159265 * radius ** 2
これを関数としてまとめておけば、必要な場所で何度でも呼び出せます。
area = circle_area(radius)
このように処理を小さな単位へ分割すると、
コードを読みやすくし、同じ処理を再利用しやすくできます。
関数(function)とは
関数は、入力された値を使って計算を行い、
基本的に1つの値を結果として返す処理です。
数学の関数と似たイメージで考えると分かりやすいでしょう。
最も簡単な関数の例
program function_example
implicit none
real :: a
real :: b
real :: answer
a = 10.0
b = 5.0
! add_numbers関数にaとbを渡します。
! 関数から返ってきた値をanswerへ代入します。
answer = add_numbers(a, b)
print *, "答え =", answer
contains
! containsより下に、
! このprogramの中で使用する関数やサブルーチンを書けます。
function add_numbers(x, y) result(sum_value)
implicit none
! xとyは関数が受け取る値です。
real, intent(in) :: x
real, intent(in) :: y
! resultで指定したsum_valueが、
! この関数から返される値になります。
real :: sum_value
sum_value = x + y
end function add_numbers
end program function_example
containsとは
containsは、programの中に関数やサブルーチンを書くときに使用します。
program sample
implicit none
! メインの処理を書く
contains
! ここに関数やサブルーチンを書く
end program sample
containsより上が主な処理、
containsより下が関数やサブルーチンの定義です。
このようにprogram内部に書いたものは内部手続きと呼ばれます。
初心者の段階では、まずこの書き方から覚えると扱いやすいです。
引数とは
関数やサブルーチンへ渡す値を引数と呼びます。
answer = add_numbers(a, b)
関数側では、渡された値をxとyとして受け取っています。
呼び出す側と関数側で変数名が同じである必要はありません。
重要なのは、渡す順番とデータ型です。
resultとは
関数では、resultを使って戻り値を保存する変数名を指定できます。
function square(x) result(answer)
implicit none
real, intent(in) :: x
real :: answer
answer = x ** 2
end function square
この場合、answerへ代入された値がsquare関数の結果として返されます。
intent(in)・intent(out)・intent(inout)
Fortranでは、引数が入力用なのか、出力用なのか、
両方なのかをintentで明示できます。
| 指定 | 意味 |
|---|---|
| intent(in) | 入力専用 |
| intent(out) | 出力用 |
| intent(inout) | 入力と出力の両方 |
初学者のうちからintentを書く習慣を付けておくと、
その引数が何のための値なのか分かりやすくなります。
円の面積を求める関数
program circle_example
implicit none
real :: radius
real :: area
print *, "円の半径を入力してください。"
read *, radius
! circle_area関数へ半径を渡します。
area = circle_area(radius)
print *, "円の面積 =", area
contains
function circle_area(r) result(a)
implicit none
! rは呼び出し側から受け取る半径です。
real, intent(in) :: r
! aが関数の戻り値です。
real :: a
! parameterを付けると定数として扱われます。
real, parameter :: pi = 3.14159265
! 円の面積 πr^2 を計算します。
a = pi * r ** 2
end function circle_area
end program circle_example
サブルーチン(subroutine)とは
サブルーチンも、処理をまとめて再利用するための仕組みです。
関数が「値を返す処理」として使われることが多いのに対し、
サブルーチンは複数の値を出力したり、一連の処理そのものを実行したりする
用途に向いています。
サブルーチンを実行するときは、callを使用します。
最も簡単なサブルーチン
program subroutine_example
implicit none
! greetというサブルーチンを呼び出します。
call greet()
contains
subroutine greet()
implicit none
print *, "Hello, Fortran!"
end subroutine greet
end program subroutine_example
サブルーチンへ値を渡す
program subroutine_input
implicit none
real :: radius
radius = 5.0
! radiusの値をshow_circle_areaへ渡します。
call show_circle_area(radius)
contains
subroutine show_circle_area(r)
implicit none
real, intent(in) :: r
real :: area
real, parameter :: pi = 3.14159265
area = pi * r ** 2
! サブルーチン内で結果を表示します。
print *, "円の面積 =", area
end subroutine show_circle_area
end program subroutine_input
サブルーチンから値を返す
サブルーチンでは、intent(out)を使って
計算結果を呼び出し側へ渡すことができます。
program subroutine_output
implicit none
real :: width
real :: height
real :: area
width = 10.0
height = 5.0
! widthとheightを入力し、
! 計算結果をareaへ受け取ります。
call rectangle_area(width, height, area)
print *, "長方形の面積 =", area
contains
subroutine rectangle_area(w, h, result_area)
implicit none
real, intent(in) :: w
real, intent(in) :: h
! result_areaは出力用です。
real, intent(out) :: result_area
result_area = w * h
end subroutine rectangle_area
end program subroutine_output
複数の値を返すサブルーチン
program multiple_output
implicit none
real :: a
real :: b
real :: total
real :: difference
a = 10.0
b = 3.0
! 合計と差の2つを受け取ります。
call calculate(a, b, total, difference)
print *, "合計 =", total
print *, "差 =", difference
contains
subroutine calculate(x, y, sum_value, diff_value)
implicit none
real, intent(in) :: x
real, intent(in) :: y
real, intent(out) :: sum_value
real, intent(out) :: diff_value
sum_value = x + y
diff_value = x - y
end subroutine calculate
end program multiple_output
intent(inout)の例
program inout_example
implicit none
integer :: number
number = 10
print *, "変更前 =", number
call double_value(number)
print *, "変更後 =", number
contains
subroutine double_value(x)
implicit none
! 元の値を受け取り、
! 変更後の値を呼び出し側へ返します。
integer, intent(inout) :: x
x = x * 2
end subroutine double_value
end program inout_example
関数とサブルーチンの違い
| 項目 | 関数 | サブルーチン |
|---|---|---|
| 定義 | function | subroutine |
| 呼び出し | 式の中などで使用 | callで呼び出す |
| 結果 | 基本的に1つの戻り値 | 複数の値を引数経由で返しやすい |
| 向いている処理 | 計算結果を返す | 一連の処理や複数出力 |
ファイル操作とは
Fortranでは、プログラムで計算した結果をファイルへ保存したり、
外部ファイルに保存されているデータを読み込んだりできます。
科学技術計算では、測定データやシミュレーション結果を扱うために重要な機能です。
ファイル操作の基本的な流れ
1. open でファイルを開く
2. read または write で読み書きする
3. close でファイルを閉じる
ファイルへ文字を書き込む
program file_write
implicit none
integer :: unit_number
! ファイルを識別する番号です。
unit_number = 10
! output.txtを書き込み用で開きます。
! status="replace"は、同名ファイルがあれば作り直します。
open(unit=unit_number, file="output.txt", &
status="replace", action="write")
! ファイルへ文字を書き込みます。
write(unit_number, *) "Hello, Fortran!"
! ファイルを閉じます。
close(unit_number)
print *, "output.txtへ保存しました。"
end program file_write
unit番号とは
Fortranでは、開いたファイルを識別するためにunit番号を使います。
open、write、closeで同じ番号を使うことで、
どのファイルを操作しているのか指定します。
open(unit=10, file="output.txt")
write(10, *) "Hello"
close(10)
newunitを使う方法
Modern Fortranでは、自分でunit番号を決めず、
newunitを使って空いている番号を自動的に割り当てることもできます。
program newunit_example
implicit none
integer :: unit_number
! 使用可能なunit番号が自動的に入ります。
open(newunit=unit_number, file="output.txt", &
status="replace", action="write")
write(unit_number, *) "Fortran file output"
close(unit_number)
end program newunit_example
数値をファイルへ保存する
program save_numbers
implicit none
integer :: unit_number
integer :: i
open(newunit=unit_number, file="numbers.txt", &
status="replace", action="write")
! 1列目にi、2列目にiの2乗を書き込みます。
do i = 1, 5
write(unit_number, *) i, i ** 2
end do
close(unit_number)
print *, "numbers.txtへ保存しました。"
end program save_numbers
ファイルから値を読み込む
例えばdata.txtに次の値が保存されているとします。
10.5
20.0
30.5
program file_read
implicit none
integer :: unit_number
real :: value1
real :: value2
real :: value3
! status="old"は既存ファイルを開く指定です。
! action="read"は読み込み専用です。
open(newunit=unit_number, file="data.txt", &
status="old", action="read")
read(unit_number, *) value1
read(unit_number, *) value2
read(unit_number, *) value3
close(unit_number)
print *, value1
print *, value2
print *, value3
end program file_read
複数列のデータを読み込む
data.txtが次の内容だとします。
1 10.5
2 20.0
3 30.5
program read_columns
implicit none
integer :: unit_number
integer :: number
real :: value
integer :: i
open(newunit=unit_number, file="data.txt", &
status="old", action="read")
do i = 1, 3
! 1列目をnumber、
! 2列目をvalueへ読み込みます。
read(unit_number, *) number, value
print *, number, value
end do
close(unit_number)
end program read_columns
ファイルの最後まで読み込む
データ数が分からない場合は、
iostatを使って読み込み状態を確認できます。
program read_until_end
implicit none
integer :: unit_number
integer :: io_status
real :: value
open(newunit=unit_number, file="data.txt", &
status="old", action="read")
do
! iostatへ読み込み結果の状態が入ります。
read(unit_number, *, iostat=io_status) value
! 0以外なら、ファイル末尾またはエラーです。
if (io_status /= 0) exit
print *, value
end do
close(unit_number)
end program read_until_end
iostatとは
iostatは、入出力処理の状態を確認するための値です。
一般的に正常なら0、ファイル末尾やエラーでは0以外になります。
open文の主な指定
| 指定 | 意味 |
|---|---|
| file | ファイル名を指定する |
| status=”old” | 既存ファイルを開く |
| status=”replace” | 新しく作成し、同名ファイルがあれば置き換える |
| action=”read” | 読み込み専用 |
| action=”write” | 書き込み専用 |
| action=”readwrite” | 読み書きの両方 |
ファイルへ追記する
既存ファイルの内容を消さず、
末尾へ追加したい場合はposition=”append”を使います。
program append_example
implicit none
integer :: unit_number
open(newunit=unit_number, file="log.txt", &
status="unknown", action="write", &
position="append")
write(unit_number, *) "新しい記録"
close(unit_number)
end program append_example
関数とファイル操作を組み合わせる
最後に、複数の測定値をファイルから読み込み、
平均値を関数で計算し、結果を別ファイルへ保存する例を紹介します。
program combined_example
implicit none
real :: data(5)
real :: average
integer :: i
integer :: input_unit
integer :: output_unit
! input.txtを読み込み用で開きます。
open(newunit=input_unit, file="input.txt", &
status="old", action="read")
! 5個の値を配列へ読み込みます。
do i = 1, 5
read(input_unit, *) data(i)
end do
close(input_unit)
! average_value関数で平均値を求めます。
average = average_value(data)
print *, "平均 =", average
! 計算結果をresult.txtへ保存します。
open(newunit=output_unit, file="result.txt", &
status="replace", action="write")
write(output_unit, *) "Average =", average
close(output_unit)
contains
function average_value(values) result(avg)
implicit none
! (:)は要素数を呼び出し側から受け取る配列です。
real, intent(in) :: values(:)
real :: avg
! sumは配列の合計、
! sizeは配列の要素数を返します。
avg = sum(values) / real(size(values))
end function average_value
end program combined_example
配列引数の「(:)」とは
次の書き方では、要素数を呼び出し側から受け取る配列を表します。
real, intent(in) :: values(:)
そのため、特定の要素数だけに固定せず、
さまざまな長さの配列へ対応しやすくなります。
配列で便利な組み込み関数
| 関数 | 意味 |
|---|---|
| sum(array) | 配列の合計を求める |
| size(array) | 配列の要素数を求める |
| maxval(array) | 最大値を求める |
| minval(array) | 最小値を求める |
よくある初学者のミス
関数を呼び出しただけで結果が表示されると思う
関数は値を返しますが、自動的に画面へ表示されるわけではありません。
answer = square(5.0)
print *, answer
サブルーチンでcallを書き忘れる
call calculate(a, b, result)
intent(out)の変数へ値を入れ忘れる
intent(out)で指定した引数は、
サブルーチン内で結果を代入してから返す必要があります。
ファイル名や保存場所を間違える
file=”data.txt”のようにファイル名だけを指定した場合、
通常はプログラムを実行している現在のフォルダを基準に読み書きされます。
ファイルをcloseし忘れる
短いプログラムでも、openしたファイルは処理後にcloseする習慣を付けると安全です。
ここまででできること
- よく使う計算を関数としてまとめる
- 複数の計算結果をサブルーチンから返す
- 測定値をテキストファイルから読み込む
- 計算結果をファイルへ保存する
- 複数行のデータを順番に処理する
- ファイルの最後までデータを読み込む
- 計算処理と入出力処理を分離する
特に科学技術計算では、
「データをファイルから読み込む → 関数やサブルーチンで計算する →
結果をファイルへ保存する」という流れがよく使われます。
まとめ
Fortranのfunctionを使うと、
入力値から計算結果を返す処理をまとめられます。
subroutineを使うと、
複数の処理をまとめたり、複数の値を呼び出し側へ返したりできます。
また、open、read、write、closeを使うことで、
テキストファイルからデータを読み込んだり、計算結果を保存したりできます。
基本文法だけでプログラムを書く場合と比べ、
関数・サブルーチン・ファイル操作を使えるようになると、
より長く実用的なFortranプログラムを整理して作れるようになります。
次の段階では、これらの機能を利用して、
行列計算、数値積分、統計処理などの科学技術計算へ発展させることができます。
