Previous: , Up: Gforth locals   [Contents][Index]


6.22.1.6 Closures

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”

ホーム・ロケーションの終了


Previous: Locals implementation, Up: Gforth locals   [Contents][Index]