FORTHの強みの一つは、メモリ領域を直接操作できる という点にある。他の言語では「配列」や「構造体」はあらかじめ用意された抽象概念であるが、FORTHではそれらを明示的に自作 する。この「データを置く場所を自分で決める」自由こそ、FORTHの低レベル言語としての魅力であり、同時に「思考を構造化する力」を鍛える最良の教材でもある。
メモリ確保の基本 ― CREATE と ALLOT
FORTHにおけるCREATEは、「新しいワードを辞書に登録し、そのワードが指すメモリアドレスを用意する」命令である。
CREATE BUF
これにより、BUFという名前のワードが辞書に登録される。この時点で、BUFはメモリアドレスを返すワード として定義される。
BUF .S
<1> 1904232
つまり、BUFを実行すると、そのメモリ位置(アドレス)がスタックに積まれる。このアドレスは、まだ何も使われていない“空の領域”である。
次に、ALLOTを使ってこの領域に一定のバイト数を確保する。
CREATE BUF 100 ALLOT
これで、BUFは100バイト分の領域を持つデータブロックを指すようになる。FORTHでは、配列も構造体もこの仕組みから構築される。
※ALLOT とは、「割り当てる」という意味の英単語。
メモリへの格納と取得 ― ! と @
確保した領域にデータを書き込むには、!(ストア)を使う。逆に読み出すには、@(フェッチ)を使う。
VARIABLE X
123 X !
X @ .
123
このVARIABLEも、内部的にはCREATEとALLOTで実装されている。
: VARIABLE ( "name" -- )
CREATE 1 CELLS ALLOT ;
配列を自作する ― CELLSとインデックス計算
FORTHでは、メモリ単位が明確である。1セル=通常は32ビットまたは64ビット(環境依存)であり、整数型データの格納にはCELLSを使うと安全である。
以下のようにして、10個の整数を格納できる配列を作ることができる。
CREATE ARR 10 CELLS ALLOT
これで、ARRは10セル分(整数10個分)の配列として機能する。各要素へのアクセスは、インデックスに応じてセル単位でアドレスを進める。
30 2 CELLS ARR + !
ここでの動作
| スタック操作 | 意味 |
|---|---|
2 CELLS |
配列内の2番目(0始まり)をセル単位でオフセット計算 |
ARR + |
ARRの基点アドレスにオフセットを足す |
! |
そのアドレスに値を格納 |
ARR 2 CELLS + @ .
30
FORTHでは配列演算すら、単純なアドレス算術の組み合わせで行う。
配列アクセスワードを定義する
FORTHの真骨頂は、これらの操作を「ワード化」して再利用できる点にある。配列アクセス専用のワードを定義してみよう。
: ARR! ( n i -- )
CELLS ARR + ! ;
: ARR@ ( i -- n )
CELLS ARR + @ ;
これで次のように使える:
10 0 ARR!
20 1 ARR!
30 2 ARR!
2 ARR@ .
30
つまり、C言語で言えば ARR[2] = 30; に相当する処理をFORTHで自作したことになる。このように、FORTHでは「必要な構造はすべて自分で定義できる」。
配列の中身を表示する
FORTHにはCのようなprint_array()という命令はないが、@(読み出し)とループ構文(DO … LOOP)を組み合わせることで、簡単に配列の中身を確認できる。
: SHOW-ARRAY ( addr count -- )
0 DO
CR I . ." : " \ 改行してインデックス表示
I CELLS OVER + @ . \ addr[i] を表示
LOOP
DROP ; \ addrを最後に消す
ARR 3 SHOW-ARRAY
0 : 10
1 : 20
2 : 30
動作
- スタックに ( addr count ) がある
- 0 DO … LOOP で I がインデックスを生成
- I CELLS OVER + で addr + i*cell
- @ で値を取り出し、. で表示
- 最後に OVERで残したaddrをDROPで消す
CREATE ... DOES>による“配列ワードの自動化”
ここまででARRのような単一配列を作ったが、複数の配列を動的に生成したい場合は、CREATE ... DOES>を使うのが便利である。
: <定義名>
CREATE (定義時の動作)
DOES> (実行時の動作)
;
これにより、**「データを持ち、実行時に固有の動作をするワード」**を作れる。いわば、FORTH流の“クラスとインスタンス”のようなものだ。
: ARRAY: ( n -- )
CREATE CELLS ALLOT
DOES> ( i -- addr )
SWAP CELLS + ;
このARRAY:を使うと、任意のサイズの配列をワードひとつで作れる。
10 ARRAY: DATA
これでDATAは、要素数10の配列として振る舞う。アクセスは次のように行う。
100 0 DATA ! \ DATA[0] = 100
200 1 DATA ! \ DATA[1] = 200
0 DATA @ . \ → 100
1 DATA @ . \ → 200
DOES>部のCELLS +が、アクセス時のアドレス計算を自動で行っている。このようにFORTHでは、言語構文を自分で定義することができる。
応用 ― 配列の初期化と走査
配列を初期化したいときも、FORTHではループを組み合わせるだけで良い。
: INIT-DATA ( n -- )
0 DO
I I DATA !
LOOP ;
10 INIT-DATA
これで DATA[0]=0, DATA[1]=1, … DATA[9]=9 が格納される。
確認は:
10 0 DO I DATA @ . LOOP
0 1 2 3 4 5 6 7 8 9
FORTHでは、このような「低レベルなループとアドレス演算の組み合わせ」によって、高度なデータ操作がシンプルに表現できる。
メモリとFORTH哲学 ― 「データは場所で考える」
FORTHのデータ構造思想は、「データ=名前ではなく、場所(アドレス) である」という前提に立っている。この考え方は、一見古風に見えるが、制御系や科学計算の世界では今も生きている。センサー値を連続的に記録する配列、シミュレーションの状態変数を保持するテーブルなど、FORTHの“場所志向の設計”は極めて直感的で、効率がよい。
この「自分で作る配列」は、次回のDOES>によるオブジェクト的構造の設計、さらには行列演算・科学データ処理への基盤となる。