PC パソコン

Fortranの関数・サブルーチン・ファイル操作

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プログラムを整理して作れるようになります。

次の段階では、これらの機能を利用して、
行列計算、数値積分、統計処理などの科学技術計算へ発展させることができます。