0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FORTH ベーシックマスター Lev 12 ー DOES>による動的構造とオブジェクト的設計

0
Last updated at Posted at 2025-12-08

前回は CREATEALLOT を使って、メモリ上に配列を自作する方法を学んだ。

FORTHの真価はここからである。単に「データを置く」だけでなく、データに固有のふるまい(動作)を与えることができる。

その中心となる仕組みが DOES>である。DOES> は、「CREATEで作ったワードが実行されたとき、何をするか」を指定する。これにより、FORTHでは“データと動作を一体化したオブジェクト”を定義できる。

CREATEの復習 ― 名前と場所を作る

まず、CREATE の基本を思い出しておこう。

CREATE BUF 100 ALLOT

これで BUF という名前が辞書に登録され、100バイト分の領域が確保される。
BUF を実行すると、そのアドレスがスタックに積まれる:

入力
BUF .
出力
1904536

FORTHでは、これが「データオブジェクトの雛形」となる。次の DOES> は、このBUFのような“データを持つ存在”に動作を付ける

DOES> ― オブジェクトに動作を与える

構文
: <定義名>
  CREATE  ... (定義時の処理) ...
  DOES>   ... (実行時の処理) ...
;

CREATE が“構造”を作り、
DOES> が“ふるまい”を定義する。

DOES> を実行すると、直前に CREATE されたワードの「実行動作」を、DOES> 以降の部分に置き換える。つまり、CREATEしたワードが別の動作を持つようになる。

配列を作るワードを作る ― 「クラス的構造」

Lev 11 で作った ARRAY: は、その典型例である。

: ARRAY: ( n -- )
  CREATE CELLS ALLOT
  DOES> ( i -- addr ) SWAP CELLS + ;

定義の動き

  1. 定義時 (ARRAY: 実行時):

    • CREATE により新しいワード(例:DATA)を登録
    • 渡されたサイズ(n)セルぶんのメモリを確保
  2. 実行時 (DATA 実行時):

    • DOES> 以降の動作が呼び出される
    • スタックのインデックス値から要素のアドレスを計算して返す

使用例

10 ARRAY: DATA
100 0 DATA !
200 1 DATA !
300 2 DATA !
0 DATA @ .  1 DATA @ .  2 DATA @ .

出力:

出力
100 200 300

DATA は、10要素の配列という「オブジェクト」 になっている。
ARRAY: は、「配列を作るクラス」 のようなものだ。

DOES>の内部動作 ― 実行部を書き換える

FORTH処理系は、DOES>を実行すると次のようなことをする。

  • CREATE によって登録されたワードには、内部的に「コードフィールド(CF)」がある。
  • 通常、このCFは DO CREATE を指し、実行時にはアドレスを返す。
  • DOES> はこのCFを書き換え、以後そのワードを実行すると DOES> 部のコードが呼ばれる。

言い換えれば、DOES> は“コンパイラ内部を書き換える命令” である。FORTHの「言語を自分で拡張できる」力は、ここにある。

DOES> は 1 つの CREATE ... DOES> に対して 1 回しか書けない

定数とカウンタを自作する ― 「ふるまい付きデータ」

(1) 定数を作る

: CONST: ( n -- )
  CREATE ,             \ 値を辞書に書き込む
  DOES> ( -- n ) @ ;   \ 実行時に読み出して返す

使用例:

100 CONST: SPEED
SPEED .

出力:

100

ここで CONST: は、DOES> によって 「値を返すオブジェクト」、まさに 定数 を自作している。

(2) カウンタを作る

: COUNTER: ( -- )
  CREATE 0 ,              \ 初期値0を記録
  DOES> ( -- )
    1 OVER +!             \ 値を1増やす
    @ . ;                 \ 現在値を表示
入力
COUNTER: C1
COUNTER: C2

C1 C1 C1
C2 C2
出力
1 2 3
1 2

それぞれ独立したカウンタとして動作している。これがFORTHにおける「インスタンス」である。

データと動作を統合する ― オブジェクト的思考

FORTHでは、CREATEが「データ構造(メモリ領域)」、DOES>が「動作(メソッド)」を担う。

この仕組みを使うと、複数のデータオブジェクトに共通の動作を与えたり、異なるふるまいを持つ構造を同時に管理できる。

\ 保存領域作成
: DATA: ( n -- addr )
  CREATE CELLS ALLOT ;

\ 書き込み専用ワード(配列オブジェクトを利用)
: STORAGE: ( 'array -- )
  CREATE ,                        \ 配列のアドレスを記録
  DOES> ( value i -- )
    @ SWAP CELLS + ! ;

\ 読み出し専用ワード(同じ配列を利用)
: DISPLAY: ( 'array -- )
  CREATE ,                        \ 配列のアドレスを記録
  DOES> ( i -- )
    @ SWAP CELLS + @ . ;
使用
5 DATA: ARR
ARR STORAGE: BOX
ARR DISPLAY: VIEW

123 2 BOX      \ ARR[2] = 123
456 4 BOX      \ ARR[4] = 456
2 VIEW         \ → 123
4 VIEW         \ → 456

ここでは、ARR に書き込む BOX と、参照する VIEW のオブジェクトが作成されている。

STORAGE:

  • 与えられた配列のアドレスを自分の中に記録(,で辞書に書き込む)。

  • 実行時に:

    1. @ でその記録された配列アドレスを取り出す。
    2. SWAP で順番を (value i) → (i value) に並べ替える。
    3. CELLS + で i番目の要素のアドレスへ進む。
    4. ! で値を書き込む。

DISPLAY:

  • STORAGE: と同じく、共有配列のアドレスを記録。

  • 実行時に:

    1. @ で配列アドレスを取り出す。
    2. SWAP で (i addr) の順に整える。
    3. CELLS + で該当要素へ。
    4. @ で値を読み出し、. で表示。

複合構造 ― 配列+関数の統合

FORTHでは、データと動作を複合化することもできる。以下は「データを持ち、平均を計算する構造体的オブジェクト」の例である。

\ --- データを使って平均を計算するオブジェクトを作る ---
: STAT: ( addr -- )
  CREATE CELLS ALLOT
  DOES> ( i addr -- n ) 
    0
    2 PICK 0 DO 
        OVER I CELLS + @ +  ( addr i ) 
    LOOP 
    ROT / SWAP DROP ;
使用
\ 5つの配列を作成
5 STAT: DATA

\ データを登録
10 0 CELLS ' DATA + !
20 1 CELLS ' DATA + !
30 2 CELLS ' DATA + !
40 3 CELLS ' DATA + !
50 4 CELLS ' DATA + !

\ データを表示
0 CELLS ' DATA + @ .
1 CELLS ' DATA + @ .
2 CELLS ' DATA + @ .
3 CELLS ' DATA + @ .
4 CELLS ' DATA + @ .

\ 5つのデータの平均を計算
5 DATA .
出力
30

' DATA は、DATA のアドレスを取り出している。
このように、DATA は「5つの値を持ち、呼ばれると平均を返す」。まさにデータと動作が一体化したオブジェクトである。

DOES> の応用 ― 言語拡張の原型

FORTHの標準ワードの多くは、実は DOES> を使って再定義できる。

たとえば VARIABLE は次のように書ける:

: VARIABLE
  CREATE 1 CELLS ALLOT
  DOES> ;

DOES> 部が空なのは、VARIABLE の実行時動作が「アドレスを返す」だけだからである。FORTHの文法要素が、すべてユーザー定義で再構築できるというのは、他の言語にはない柔軟性である。

まとめ ― DOES>が拓く“構造と思考の一致”

構文要素 意味
CREATE 名前とメモリ領域を作る(構造)
DOES> その構造のふるまいを定義する(動作)
, 定義時に値を書き込む
@ / ! メモリからの読み出し/書き込み
CELLS セル単位のアドレス計算

FORTHでは、CREATE … DOES> によって「クラス → インスタンス」「構造 → 動作」の関係をすべて自作できる。この仕組みは、C言語の構造体でも、Lispのオブジェクトでもなく、FORTHそのものが“メタ言語”として動作していることを示している。

演習課題

  1. CREATE … DOES> を使って「1次元ベクトル」を定義し、

    • 要素設定 (SET-VEC)
    • 要素取得 (GET-VEC)
    • スカラー倍 (SCALE-VEC)
      を実装せよ。
  2. CONST: を改良し、定義時にラベル文字列を保存して、
    実行時に「名前: 値」を表示するようにせよ。

  3. 複数のCOUNTER:を生成し、それぞれが独立して動作することを確認せよ。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?