以下はご提示の文章を、**「だ・である調」**に統一した改稿版である。語尾のブレをなくし、説明文としての一貫性を保つよう整えている。
FORTHには複数のスタックがある
FORTHは「スタック指向言語」と呼ばれるが、実際には1つのスタックだけで動作しているわけではない。FORTHシステムの内部には、次の3種類のスタックが存在している。
| スタック名 | 主な用途 |
|---|---|
| データスタック | 演算・引数・戻り値などの通常のデータ処理に用いる |
| リターンスタック | 制御構造やサブルーチンの戻りアドレス管理 |
| 浮動小数点スタック | 実数(浮動小数点)演算用の独立したスタック |
普段われわれが 1 2 + . のように扱っているのはデータスタックである。しかし、FORTHの制御構造(DO, LOOP, IF, THEN など)を支えているのは、もう一つのリターンスタックである。
さらに、科学技術計算や実数処理を行う場合には、データスタックとは独立した浮動小数点スタックが使用される。このFスタックについては第7回以降で詳しく扱うが、本章ではまず「制御を支えるもう一つのスタック」であるリターンスタックを中心に解説する。
リターンスタックの基本的な役割
リターンスタック(return stack)は、ワード(FORTHにおける手続き)呼び出しやループ制御の際に、「どこに戻ればよいか」という制御情報を保持するスタックである。
FORTHでワードが呼び出されるたびに、呼び出し元の戻り番地がリターンスタックに積まれ、ワードの実行が終わるとそこから取り出されて処理が戻る。この仕組みによって、FORTHは非常に小さな仕組みでネストした呼び出しや再帰呼び出しを実現している。
リターンスタック操作の基本ワード
通常、FORTHプログラマはリターンスタックを直接操作する必要はない。しかし、FORTHではリターンスタックも「操作可能な構造」として明示的にアクセスできるようになっている。
| ワード | スタック効果 | 内容 |
|---|---|---|
>R |
( x -- ) (R: -- x ) | データスタックのトップをリターンスタックに移す |
R> |
( -- x ) (R: x -- ) | リターンスタックのトップをデータスタックに戻す |
R@ |
( -- x ) (R: x -- x ) | リターンスタックのトップを読む(値は保持される) |
以下は典型的な利用例である。
: TEST-R ( n -- )
DUP >R
CR
." Data stack: " . CR
." Return stack: " R@ . CR
R> DROP ;
10 TEST-R
Data stack: 10
Return stack: 10
ここでは >R によって値10がリターンスタックに退避され、R@ によってその値を参照し、最後に R> で戻すことで整合性を保っている。
制御構造とリターンスタックの関係
DO ... LOOP 構文を例に取ると、その内部ではループ制御のための「開始値」「終了値」および「実行制御の復帰先」がリターンスタックに積まれる。
DO 実行時の動作
DO が実行されるとき、データスタック上には (limit start -- ) の2つの値がある。この2つを取り出し、FORTHシステムは次の3つの情報をリターンスタックに積む。
| 項目 | 内容 | 用途 |
|---|---|---|
| return address |
LOOP 終了後に戻るアドレス |
実行制御の復帰先 |
| limit | ループの終了値 | 終了判定の境界 |
| index(start) | 現在のカウンタ値 |
I によって参照されるループ変数 |
LOOP 実行時
LOOP に到達すると、FORTHは次の処理を行う。
-
index を 1 増加
-
limit と比較
- index < limit → return address にジャンプ(ループ継続)
- index >= limit → 制御フレームを破棄してループ終了(UNLOOP相当)
I, J, K ワードは、このリターンスタック上の index 情報を参照する仕組みである。この方法により、多重ループの変数参照も自然に実現されている。
このように、FORTHのループ構造はリターンスタックによって完全に制御されている。これにより、FORTHは非常にコンパクトな制御構文を提供しているのである。
再帰呼び出しとリターンスタック
FORTHでは、ワードが自分自身を呼び出す「再帰処理」が自然に記述できる。この動作を支えているのもリターンスタックである。
ワードが呼び出されるたびに戻りアドレスがリターンスタックに積まれ、終了すると取り出される。この積み重ねによって再帰は実現される。
: FACT ( n -- n! )
DUP 1 > IF
DUP 1- RECURSE *
THEN ;
5 FACT . → 120
RECURSE は「このワード自身を再び呼び出す」ことを意味する。呼び出しが深まるたびに戻りアドレスが積まれ、終了とともに一段ずつ戻る構造になっている。
リターンスタックの注意点
リターンスタックは制御構造にとって極めて重要な領域である。したがって、>R や R> を制御構造内部で不用意に用いると、制御情報を破壊し、プログラムの誤動作を招く。
: BAD ( -- )
10 0 DO
I >R \ リターンスタックに値を積む
CR ." Return Stack: " R@ .
\ 積んだ値を消さずにループに戻る
LOOP
;
このようなコードは「unstructured return」や「stack imbalance」などのエラーを引き起こすため、厳禁である。
原則
- リターンスタックの利用は制御構文に触れない位置で行い、
>RとR>を必ず対にすること。
一時的な値退避テクニック
リターンスタックは一時変数の代用としても使える。
: SWAP-SQUARE ( a b -- a² b² )
>R \ bをリターンスタックに退避
DUP * \ a²
R> DUP * \ b²
;
3 4 SWAP-SQUARE . . → 9 16
重ねて注意するが、制御構造と干渉する位置で使用するとループ情報を破壊するため、使用箇所には十分な配慮が必要である。
FORTHのスタック構造
ここで整理すると、FORTHには次の3つのスタック構造がある。
| スタック | 役割 | 主なワード |
|---|---|---|
| データスタック | 通常の値・アドレス管理 |
DUP, SWAP, OVER, +, -
|
| リターンスタック | 制御構造・再帰・戻り管理 |
>R, R>, R@, UNLOOP, RECURSE
|
| 浮動小数点スタック | 実数演算専用 |
F+, F-, F*, F/, S>F など |
リターンスタックはFORTHの制御構造を支える中心的な役割を果たしており、ループ・条件分岐・再帰のすべてがここに依存している。3つのスタックは互いに独立しており、データスタック操作がリターンスタックに干渉することはない。また、浮動小数点演算も独立して行われるため、安全である。
FORTHの強みは、このような低レベルの仕組みをシンプルで透明な形で学べる点にある。「制御もデータもスタック上で完結する」という思想を理解することが、FORTH的思考を身に付ける第一歩である。