以下のコマンドを使用して、 (データのない)リンク・リストの構造体を定義できます(訳注: これは構造体テンプレートを定義するだけです。
構造体変数とするには別途 %alloc
等する必要があります):
struct cell% field list-next end-struct list%
スタック上のリスト・ノードのアドレスを使用して、 list-next
を使用して次のノードのアドレスを含むフィールドのアドレスを計算できます。 たとえば、 以下のようにしてリストの長さを決定できます:
: list-length ( list -- n ) \ "list" is a pointer to the first element of a linked list \ "n" is the length of the list 0 BEGIN ( list1 n1 ) over WHILE ( list1 n1 ) 1+ swap list-next @ swap REPEAT nip ;
list% %allot
を使用すると、 ディクショナリー内にリスト・ノード用のメモリーを確保でき、 これにより、
リスト・ノードのアドレスがスタック上に残ります。ヒープ上で同様の割り当てを行うには、 list% %alloc
を使用できます(または、allocate
のようなスタック効果(つまり、ior を使用)が欲しい場合は、 list%
%allocate
を使用します)。 リスト・ノードのサイズは list% %size
で取得でき、 そのセル・アライメントは
list% %alignment
で取得できます。
注意: 標準 Forth では、 create
されたワードの本体は aligned
されていますが、 必ずしも
faligned
されている訳ではない事に注意してください。したがって、 以下のようにすると:
create "name" foo% %allot drop
この場合、 foo%
に割り当てられたメモリーは、 foo%
に文字フィールドやセル・フィールドや2倍長整数フィールドのみが含まれている場合にのみ、 name
の本体から開始されることが保証されます。 したがって、 浮動小数点数が含まれる場合は、以下を使用することをお勧めします
foo% %allot constant "name"
以下のように、 構造体 foo%
を別の構造体のフィールドとして含めることができます:
struct ... foo% field ... ... end-struct ...
構造体をいちから構築する代わりに、 既存の構造体を拡張できます。 たとえば、 上記例で定義したような、 データのない単純なリンク・リストはほとんど役に立ちません。 これを以下のように、 整数の値を持つリンク・リストに拡張できます:31
list% cell% field intlist-int end-struct intlist%
intlist%
は、 list-next
と intlist-int
の 2
つのフィールドを持つ構造体です。
以下のように、 n 要素の foo%
型を含む配列型を指定できます:
foo% n *
この配列型は、 通常の型を使用できる場所であればどこでも使用できます(例: field
を定義する場所や %allot
を使用するとき)。
最初のフィールドは構造体のベース・アドレスにあり、 この、 最初のフィールドのワード(例:
list-next
)は実際にはスタック上のアドレスを変更しません。 あなたは実行時間と領域の効率を考慮して、
最初のフィールドのワードを取り除きたいとと思うかもしれません。 しかし、 構造体パッケージがこの場合を最適化するため、 そのは必要ありません。
最初のフィールドのワードをコンパイルする場合、 コードは生成されません。 したがって、 読みやすさと保守性を考慮して、
最初のフィールドにアクセスするときにその最初のフィールドのワードは含めるべきです。
この機能は「拡張レコード」(extended records)とも呼ばれます。 これは、 オベロン・プログラミング言語が成した主な革新です。 言い換えれば、 この機能を Modula-2 に追加することで、 Wirth は新しい言語を作成し、 新しいコンパイラの記述等を行いました。 この機能を Forth に追加するには、 数行のコードが必要なだけでした。