オブジェクト指向 Fortran 90/95 プログラミングについて
- 小高 正嗣, 森川 靖大
- 2005/08/06 (森川 靖大) 加筆
- 2005/08/04 (小高 正嗣) 新規作成
概要
森川が 参考図書 を読んで面白そうと思ったオブジェクト指向的 Fortran90/95 プログラミングについて紹介し, それに関する議論を行った.
以下はその際の紹介に関する記録である. なお, 言葉に関してはオブジェクト指向的 用語や Fortran 用語, 中にはオブジェクト指向スクリプト言語 Ruby の用語まで 混ざったりしていて読みづらい部分も多いかと思うがご了承いただきたい. (修正していただけるとよりありがたい…).
日時・場所・参加者
- 日時
- 2005 年 8 月 4 日(木) 13:30 -- 15:30
- 場所
- 北大理学8号館 8-2-08
- 参加者
- 石渡正樹, 小高正嗣, 杉山耕一朗, 山田由貴子, 森川靖大, 高橋芳幸, 北守太一, 谷口博, 林祥介
参考図書
Ed Akin, 2003: 「Object-Oriented Programming Via Fortran 90/95」 Cambridge University Press. ISBN 0-521-52408-3 |
- ドラフト版は http://www.owlnet.rice.edu/~mech403/Fortran_90/OOP_w_F90_book.htm より無償でダウンロード可能
オブジェクト指向とは
オブジェクト指向の 3 要素について簡単に解説. 詳しいことはオブジェクト指向に関する書籍を参照のこと.
- 情報隠蔽 (encapsulation)
- 継承 (inheritance)
- 多態性 (polymorphism)
クラスと情報隠蔽
3 章 Object Oriented Programming Concepts では, 主に Fortran90 で クラスの作り方, 情報隠蔽の手法 について述べている.
具体例としては 3.2.1 Example Date, Person, and Student Classes のサンプルプログラムを参照のこと.
クラスの作り方
- クラスとは?
- データ (変数定義) とその変数に対する手続きをまとめたもの
- モジュールをクラスに
- モジュール内において構造体を宣言し, その構造体に関する 手続きを全てそのモジュールで用意する.
- インスタンス変数
- 説明
- クラスの初期化メソッドを呼ぶことによっていくつも生成できる変数
- 実装の方法
- 構造体の要素として実装
- 説明
- クラス変数
- 説明
- クラス内で保持し, 個々のインスタンスとして複製出来ない変数
- 実装
- save 属性を持つ変数として実装 (構造体には格納しない)
- 説明
- 手続きに関する制約
- 初期化用メソッド (サブルーチンでも関数でも良い) を用意する.
- 構造体を返すということが主要な要素.
- 必要な最低限の値を引数として受け, それらの値の妥当性をチェックする.
- 返る構造体は, それ以外のメソッドの引数として代入される.
- 初期化用メソッド (サブルーチンでも関数でも良い) を用意する.
情報隠蔽の手法
まず, 上記のクラスの作り方を参考にクラスを作る. その上で情報隠蔽するための手法を記す.
- 構造体定義の際に, type のスコープ内に private を記述し,
構造体の要素への外部からのアクセスを禁止する.
ここで注意して欲しいのは, 決して構造体本体を private するわけでは
ないこと.
- このようにすることで, 外部からの構造体内部へのアクセスが 禁止される. つまり, 内部のデータ構造について知ることが できなくなる.
- その上で, 構造体内部を操作するためのメソッドを用意する. 当然それらメソッドを介さない限り構造体内部の値を 参照・変更できないのでメソッドを充実させないと単に使えないクラスとなることも 考えられるので注意.
- このご利益として主に以下の 2 つが考えられる.
- 構造体の要素 (つまりデータ構造) に関して外部は知ることが出来ず, 知る必要も無いため, その実装方法に関して制約を受けない. せいぜい制約を受けるのはメソッドの仕様ぐらい.
- メソッドを介さないと構造体の要素を変更できないため, 予期しない操作を抑制できる.
gtool4 での実装例
実は gtool4 でも上記のクラスが実装されている. gt4_history モジュールや gtdata_generic + gtdata_types モジュール an_generic + an_types モジュールがそれである.
例えば gt4_history では GT_HISTORY という構造体が用意されており, HistoryCreate が初期化メソッドにあたる. (ただし, デフォルトは gt4_history 内部に用意されている save 変数が利用されるため, 出力ファイルを 1 つにしている場合はあまりなじみが無いかも しれない).
継承と多態性
6 章 Inheritance and Polymorphism において継承の実装方法や 多態性の実装方法について述べている.
継承の実装方法
継承の定義
まず, ここで森川の考える継承の定義を述べる. (Ruby で勉強した部分が多く, 継承としての一般的定義から外れている可能性も あるので注意).
- 継承の定義 (偏見含む)
- 原則的にベースとなるクラスのメソッドおよびインスタンス変数を全て継承する.
- 新しいメソッド, インスタンス変数を追加することが可能
継承の F90 での実装
残念ながら, Fortran90/95 ではこれらをお手軽に実装するための仕組みは 存在していないようである. Akin (2003) の中でも, use の利用を継承と 称する部分はあるが, (もちろんまともに継承を実装する場合に use は不可欠 ではあるが), ただ use するだけでは, あくまでもベースのクラスをそのまま利用 するだけで, 新たなクラスへの継承ということにはならない.
では, 上記の継承を Fortran90/95 で実装するにはどうしたらいいかを 考察し, 以下にまとめた.
- Ruby で実装される「継承」の実装に関して
- 新しいクラスの type 内にベースのクラスの構造体を含む.
- ベースのクラスのサブルーチンと関数を全てインポートし, 新たに作成する構造体を与えたときの基本動作をベースのクラスの 構造体を与えたときと同じにする. (もちろん上書きは可能である).
なお, これはあくまで単純継承の話であり, 多重継承の場合は 複数のベースクラスの構造体を含んだり, 複数のクラスのメソッドを インポートするなども考えられる. (多重継承を是とするか否とするかは かなり悩ましい問題なのでここでは触れない).
例えば, 上記に比較的近いのは Figure 6.9 だが, この場合も正しく継承される には, class_Employee の全てのメソッド (サブルーチン及び関数) が type Manager に対して全てインポートされなければならない.
ベースのクラスを解析して雛形を作るぐらいは可能かもしれないが, やっぱり Ruby ほど簡単ではない.
結論としては, やっぱり F90 での継承の実装はなかなか困難であると いえるだろう.
多態性の実装方法
総称名称を用いた多態性の実装
クラスの作り方でメソッドを作成してきた場合, メソッドは 属するモジュールが持つ構造体を引数にとるので, interface を利用して 1つの総称名にまとめることが可能なので, そういった意味での 多態性の実装は Fortran90/95 では容易である.
また interface 文は同じ総称名称の定義を行う場合であっても, 複数箇所で 記述することが許されているので, 各個の クラスにて interface で一般的な名称を与え, それらのクラスを 一箇所のライブラリにしてまとめておけば, 使う側はある一般的な 名称のメソッドさえ知っておけば, 後はそのメソッドは与えられた 構造体に応じて振る舞い変化させる.
異なる型の変数に大して同様な挙動をするサブルーチン・関数の開発
Fortran は Perl や Ruby といったスクリプト言語と異なり, 変数には 必ず型が存在する. そのため, 例えばほとんど挙動が同じに場合で あっても, 単精度実数と倍精度実数とで別々のサブルーチン・関数を 整備しなければならない.
他にも, 配列データを入出力するサブルーチン・関数の場合でも, 1, 2, 3, ... 次元と引数の型が異なるものをとろうとする場合, やはり 個々に手続きを用意しなければならない.
Akin (2003) では, こういったものをテンプレート (雛形) ファイルから 生成することでその開発・整備のコストを減らすことを提案している. (この方法が elegant でないことは Akin 自身も述べている).
gtool4 での試み
gtool4 では m4 というマクロプロセッサ (C プリプロセッサでもそれなりに 似たようなことはできるはずだが) を使っている. これにより, 上記のような単精度実数と倍精度実数をとる サブルーチンの作成を 1 つの雛形から可能にしたり, 1つの雛形から 1 次元 〜 30 次元までのサブルーチンを自動生成したり している.
もちろん Fortran 的に異物が入るので少々コードは見づらくなるが, かなりコストは削減される.