Fortranで装置番号ではなくファイル名に基づいてファイルを操作する

# 概要
`inquire`文を利用して，ファイル名と装置番号を紐付けることで，明示的に装置番号を扱わないようにします．

# テスト環境
- Windows 10
- gfortran 10.3.0
- intel fortran 2021.5.0
- nag fortran 7.1

# Fortranにおけるファイル操作
Fortranは，その歴史的経緯から，ファイルやその他入出力機器を**装置**と称し，各装置に番号をふって管理します．

例えば，標準出力には5番，標準入力には6番という装置番号が使われることが多くありました．現在は，`output_unit`, `input_unit`という既定の定数が設けられています．

ファイルも装置として扱われるので，ファイルを開く場合には装置番号とファイルを紐付ける必要があります．下記の例は，`test.txt`という名前ファイルを開き，`test`という文字列を書き込んで閉じる処理です．

```Fortran
use, intrinsic :: iso_fortran_env
integer(int32) :: unit_number
character(256) :: iomsg

open (newunit=unit_number, file="test.txt")
write(unit_number, *) "test"
close(unit_number)
```

装置を開くには`open`文，装置を閉じるには`close`文を用います．装置番号は，ファイルを開く際にユーザ側で決める必要があり，結構面倒だったのですが，Fortran 2008からは空いている番号に割り振る機能が追加されました．`newunit`指定子がそれに相当します．

ファイルの状態（読み取り専用か，ファイルは既に存在しているか等）を細かく指定することもできます．

```Fortran
use, intrinsic :: iso_fortran_env
integer(int32) :: unit_number, iostat
character(256) :: iomsg

open (newunit=unit_number, file="test.txt", action="readwrite", status="unknown", iostat=iostat, iomsg=iomsg)
write(unit_number, *) "test"
close(unit_number)
```

装置番号は，C言語における`FILE`型のポインタ変数に近いと考えると，理解が容易かも知れません．

`newunit`でファイルの装置番号を決めなくてもよくなったとはいえ，装置番号を管理するために変数が余分に必要になります．もう少し簡単に，ファイル名だけでファイルを開いたり，書き込んだりしたいと思うのは自然なことだと思います．

`inquire`文を用いると，それが実現できます．

# `inquire`文
問い合わせ文とも呼ばれます．`open`文と対になるような文で，装置番号やファイル名から状態（読み取り専用として開かれているか等）を取得します．

`test.txt`という名前のファイルに割り振られた装置が開かれているか，その装置番号は何番かを確認するには，下記のプログラムを実行します．

```Fortran
use, intrinsic :: iso_fortran_env
integer(int32) :: unit_number
logical :: is_unit_opened

inquire (file="test.txt", opened=is_unit_opened, number=unit_number)
```

`inquire`の問い合わせ指定子は非常に多く，規格では40個ほどの問い合わせ指定子が定義されています．指定子を一つずつ解説することは，分量の都合でできないので，気になった方は調べてみてください．

`inquire`文を利用して，ファイル名から装置番号を返す関数を作る事で，明示的に装置番号を扱わないようにします．

# ファイル名から装置番号を返す関数
ファイル名を受け取り，装置番号を返す関数を作成します．動作は非常に単純です．

まず，`inquire`文を用いて引数で取得したファイルが開かれているかの状態と，その装置番号を取得します．ファイルが開かれているのであれば，装置番号が正しく取得されているので，`return`で呼出し元に戻ります．ファイルが開かれていない場合は，`open`文を利用してファイルを開き，装置番号は`newunit`を利用して自動で割り振ります．

```Fortran
!>ファイル名と対応する装置番号を返す．
!>ファイルが存在しない場合は新しく装置を開く．
function get_unit_number(filename) result(unit_number)
    implicit none
    character(*), intent(in) :: filename
        !! ファイル名
    integer(int32) :: unit_number
        !! 戻り値
        !! ファイル名に紐付いた装置番号

    logical :: is_unit_opened

    ! 引数で指定したファイル名のファイルが，装置として開かれているかを問い合わせる
    inquire (file=filename, opened=is_unit_opened, number=unit_number)

    ! 開かれていたらunit_numberが取得されているのでreturnする
    if (is_unit_opened) return

    ! 開かれていなかったらファイルを開き，その装置番号を返す
    open (newunit=unit_number, file=filename)
end function get_unit_number
```

利用する際は，`write`文や`close`文の装置番号指定を，関数呼び出しに変更するだけです．ファイルが開かれていなくても，関数の中で開かれるので`open`文は不要です．

```Fortran
write (get_unit_number("test.txt"), *) "test"
close (get_unit_number("test.txt"))
```

同じような動作をする`open`関数が，[Fortran標準ライブラリ](https://github.com/fortran-lang/stdlib)のioモジュールに定義されています．

```Fortran
use :: stdlib_io
write (open("test.txt", "w"), *) "test"
```

ただし，`open`関数には，既に開かれているファイルの装置番号を返す機能がないため，装置番号を戻り値として受け取る変数が必要になります．

# まとめ
装置番号を明示的に扱わないようにするため，ファイル名から装置番号を返す関数を作成しました．

既に開かれているファイルの装置番号の取得には`inquire`文，新しく装置を開く場合の装置番号の決定には，`open`文の`newunit`指定子を利用しました．

「装置番号を変数で取り扱うのが面倒だ」という要求を，関数で解決するのは，関数の管理という新たなめんどくささを導入しただけのような気もしますが，[Fortran Package Manager](https://github.com/fortran-lang/fpm)形式のパッケージとして公開する，あるいは[Fortran標準ライブラリ](https://github.com/fortran-lang/stdlib)に取り込んでもらうなど，より簡便に利用できるような仕組みを考えています．