Gforth は基礎的なクロージャ(closure)も提供します。 クロージャは、
引用(quotation)(see Quotations)とローカル変数の組み合わせです。 Gforth のクロージャには、
クロージャの実行時に値が入力されるローカル変数があり、 トランポリン xt (trampoline xt)が生成されます。 そのトランポリン xt を
execute すると、 ローカル・スタック上のクロージャのローカル変数にアクセスして、 クロージャのコードが実行されます。
クロージャのローカル変数の変更は永続的ではありません。 つまり、 クロージャが EXIT
されると、 変更された値は失われます。
[{:
( – hmaddr u latest latestnt wid 0 ) gforth-experimental “start-closure”
クロージャを開始します。 クロージャはまず、 クロージャのために使用するローカル変数フレームを宣言し、
次にそれらのローカル変数で実行されるコードを宣言します。 クロージャは引用(quotations)のように ;]
で終わります。
ローカル宣言は、 クロージャ・ローカルが作成される場所に応じて終了します。 実行時、 クロージャは トランポリン xt として作成され、
スタックからローカル変数・フレームの値を埋めます。 xt の実行時に、 ローカル変数・フレームがローカル・スタックにコピーされ、
クロージャのコード内で使用されます。 戻った後、 これらの値はローカル・スタックから削除され、 クロージャ自体は更新されません。
:}l
( hmaddr u latest latestnt wid 0 a-addr1 u1 ... – ) gforth-1.0 “close-brace-locals”
クロージャ・ローカルの宣言を終了します。 クロージャはローカル・スタックに割り当てられます。
:}d
( hmaddr u latest latestnt wid 0 a-addr1 u1 ... – ) gforth-1.0 “colon-close-brace-d”
クロージャ・ローカル宣言を終了します。 クロージャはディクショナリーに割り当てられます。
:}h
( hmaddr u latest latestnt wid 0 a-addr1 u1 ... – ) gforth-1.0 “colon-close-brace-h”
クロージャ・ローカル宣言を終了します。 クロージャーはヒープに割り当てられます。
:}h1
( hmaddr u latest latestnt wid 0 a-addr1 u1 ... – ) gforth-1.0 “colon-close-brace-h”
クロージャ・ローカル宣言を終了します。 クロージャーはヒープに割り当てられます。
:}xt
( hmaddr u latest latestnt wid 0 a-addr1 u1 ... – ) gforth-1.0 “colon-close-brace-x-t”
クロージャ・ローカル宣言を終了します。 クロージャは xt によってスタック上に割り当てられるため、 クロージャの実行時のスタック効果は
( xt-alloc -- xt-closure )
となります。
>addr
( xt – addr ) gforth-experimental “to-addr”
(free-closure
から呼び出されます)ヒープ上のクロージャの xt を addr に変換し、 free
に渡すことでクロージャを削除できます。
free-closure
( xt – ) gforth-internal “free-closure”
ヒープに割り当てられたクロージャを解放(free)します
: foo [{: a f: b d: c xt: d :}d a . b f. c d. d ;] ; 5 3.3e #1234. ' cr foo execute
上記 foo
は、 単一セルと浮動小数点数と2倍長整数と xt を含むクロージャをディクショナリー内に作成し、呼び出し時に最初の 3
つの値を出力後に xt を実行します。
これにより、 Algol コンパイラーをテストするために 1964 年にドナルド・クヌースが提案した “Man or boy test” を実装することができます(訳注: 手元ではサッパリ動いてない(0.7.9_20240418, 2024.7))
: A {: w^ k x1 x2 x3 xt: x4 xt: x5 | w^ B :} recursive k 0<= IF x4 x5 f+ ELSE B k x1 x2 x3 action-of x4 [{: B k x1 x2 x3 x4 :}L -1 k +! k B x1 x2 x3 x4 A ;] dup B ! execute THEN ; : man-or-boy? ( n -- ) [: 1e ;] [: -1e ;] 2dup swap [: 0e ;] A f. ;
場合によっては、 クロージャを変更するには永続的なストレージが必要です。 複数のクロージャがその永続ストレージを共有する可能性さえあります。 上の例では、 外部プロシージャのローカル変数がこれに使用されていますが、 場合によっては、 クロージャが外部プロシージャよりも長く存続します。 特に、 ディクショナリーまたはヒープ上に割り当てられたクロージャは、 親プロシージャより長く存続するように設計されています。
これらについては、 クロージャのように割り当てられるホーム・ロケーション(home locations)がありますが、 そのコードは作成時に直接実行され、 ホーム・ロケーションのアドレスを提供する必要があります。
: bar ( a b c -- aaddr baddr caddr hl-addr ) <{: w^ a w^ b w^ c :}h a b c ;> ;
この例では、 ヒープ上に 3 つのセルを持つホーム・ロケーション(home location)を作成し、 3
つのロケーションのアドレスとホーム・ロケーションのアドレスを返します。 このアドレスは、 ホーム・ロケーションが不要になったときに
free
するために使用できます。
<{:
( – hmaddr u latest latestnt wid 0 ) gforth-experimental “start-homelocation”
ホーム・ロケーション(home location)の開始
;>
( – ) gforth-experimental “end-homelocation”
ホーム・ロケーションの終了