以下のコマンドを使用して、 (データのない)リンク・リストの構造体を定義できます(訳注: これは構造体テンプレートを定義するだけです。
構造体変数とするには別途 %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 に追加するには、 数行のコードが必要なだけでした。