Fortranで「自分自身を呼び出す」という冒険:RECURSIVE属性とスタックの守り方
やあ、エンジニア諸君。宇宙開発の現場で数値シミュレーションを回し続け、コンパイラの最適化レポートと何十年も格闘してきた私が、今日は「再帰(Recursion)」について話をしよう。
PythonやC言語で育った君たちにとって、再帰関数は「木構造の探索」や「クイックソート」で当たり前のように使う道具かもしれない。しかし、Fortranの世界では少しだけ「お作法」が必要だ。なぜなら、Fortranはもともと「計算の効率」を極限まで追求するために設計された言語であり、再帰はデフォルトでは禁止されているからだ。
「えっ、禁止?」と驚くかもしれないが、安心してほしい。その理由と、安全に使いこなすための知恵を伝授しよう。
—
1. なぜFortranには `RECURSIVE` が必要なのか?
Fortranのコンパイラにとって、一番の敵は「予測不能なメモリ消費」だ。昔のFortranでは、すべての変数のメモリアドレスがコンパイル時に確定していた。これにより、計算機は無駄なオーバーヘッドなしに最速でメモリを叩くことができたんだ。
しかし、再帰呼び出しを許すと、関数が自分自身を呼び出すたびにスタックメモリが消費され、実行時までメモリ使用量が予測できなくなる。そこでFortranは、「再帰を使いたいなら、わざわざ `RECURSIVE` って宣言してね。そうすればコンパイラもそのつもりで準備するから」というルールにしたのさ。
まずは基本の書き方
例えば、階乗を計算するコードを書いてみよう。
! RECURSIVEキーワードを先頭に置くのが鉄則!
RECURSIVE FUNCTION factorial(n) RESULT(res)
INTEGER, INTENT(IN) :: n
INTEGER :: res
IF (n <= 1) THEN res = 1 ELSE ! 自分自身を呼び出す。これが再帰の醍醐味だ res = n factorial(n - 1) END IF END FUNCTION factorial ここで重要なのは、`RESULT(res)` を使っていることだ。再帰関数では、関数名そのものに値を代入する古い書き方よりも、このスタイルの方が読みやすく、モダンなFortranの作法として推奨されている。 ---
2. スタックオーバーフローという「深淵」
さて、ここからが現場の知恵だ。再帰の深さが深くなると、OSがプログラムに割り当てた「スタック領域」を食いつぶし、セグメンテーション違反(Segmentation Fault)でプログラムが即死する。
C言語なら `ulimit -s unlimited` などで解決することもあるが、Fortranの数値計算では、コンパイルオプションでコード自体を「スタックに優しい形」に最適化させるのがスマートだ。
コンパイラオプションの調整(例:gfortranの場合)
もし君が大規模な再帰処理を回すなら、以下のフラグを試してみてほしい。
- `-fstack-arrays`: ローカルの大きな配列をスタックに置かず、ヒープ領域を上手く使わせるためのオプションだ。
- `-fmax-stack-var-size=…`: 再帰呼び出しでスタックが溢れそうなとき、この値を調整することで挙動を制御できる。
ビルドコマンドはこんな感じだ:
最適化をかけつつ、再帰呼び出しを許容する安全な設定
gfortran -O3 -fstack-arrays -o simulation main.f90
—
3. 実践:再帰を避けるという選択肢
最後に、元設計者として一つだけアドバイスしておこう。「再帰が必要な場面でも、ループで書けるならループで書け」というのが、Fortran界隈の不文律だ。
Fortranのコンパイラは、`DO` ループの並列化やベクトル化(SIMD)において神がかった最適化を見せる。再帰関数は、コンパイラから見ると「どこで並列化していいか分からない壁」のように映ることがある。
もし君が書いているプログラムが「単なる数列の計算」なら、再帰よりもループの方が10倍、いや100倍速くなる可能性がある。再帰を使うのは、あくまで「アルゴリズムの構造上、再帰でないと記述が困難なとき」だけに留めておくのが、高性能計算を支えるエンジニアの矜持というものだ。
—
今日のまとめ
1. 再帰を使うときは、必ず `RECURSIVE` 属性を忘れずにつけること。
2. `RESULT` 節を使って戻り値を定義しよう。
3. スタックオーバーフローが怖いときは、コンパイラのオプションでメモリ管理を制御する。
4. でも一番の最適化は、再帰を使わずにループで解決すること!
どうだい?少しはFortranの「こだわり」が分かってもらえたかな。この言語は一見不器用に思えるかもしれないが、君が書いたコードの裏側で、計算機が唸りを上げて最高のパフォーマンスを絞り出してくれるはずだ。
次は、配列の「列優先順位」を活かした高速化について話そうか。あれこそが、Fortranが世界を制した理由なんだ。また次の講義で会おう!

コメント