最近の更新 (Recent Changes)

2014-01-01
2013-01-04
2012-12-22
2012-12-15
2012-12-09

Wikiガイド(Guide)

サイドバー (Side Bar)

--
← 前のページに戻る

5.3.2 クロージャを管理するリスト

クロージャは以下に示すリストによって管理されます。

主にクロージャの追加・削除のときにこれらのリストが役立つことになるのです。


クロージャ・リスト
最新クロージャ番号
変数リスト
環境リスト
アドレスリスト
アドレス・オフセット

5.3.2.1 クロージャ・リストと最新クロージャ番号

コンパイル時に新しくクロージャが生成されると、最新クロージャ番号Nを使って"closureN"が作成され、クロージャ・リストに登録されます。 新しいクロージャの生成後には最新クロージャ番号は+1されます。

例えば、次に示すのはclosure0の中から呼ばれた関数(クロージャ)から呼ばれたclosure1から、さらにclosure2が呼ばれたときのクロージャ・リストを表します。


 closure2 → closure1 → closure 0 → NIL

closure0は、グローバルな環境を保持しているためClosure Basicの初期化処理の中で登録されます。

コンパイル時に、新たに関数を呼び出すと新しいクロージャが作成され、新しい変数の登録と実行コードをそのクロージャの中に設置していきます。 クロージャの処理の設定がすべて完了すると、そのクロージャはクロージャ・リストから削除されます。

そのため、最も左端に登録されているクロージャは、現在のクロージャとしてカレント・クロージャと呼ぶこととします。

5.3.2.2 変数リスト

変数を管理するリストです。

管理される変数はクロージャ間を超えて、システム全体の変数について管理します。

このリストに登録される値は、新しく作成された変数ごとに(変数名, 所属クロージャ名, アドレス, 値の長さ)を登録します。


   (変数名1, 所属クロージャ名, アドレス, 値の長さ) → (変数名2, 所属クロージャ名, アドレス, 値の長さ) ...

"値の長さ"は、通常の変数では1です。 しかし、配列変数の場合にはこの値が配列の長さとして設定されます。

Closure Basicのコンパイラはこの変数リストの中から変数を探し出して使用します。

変数を探すときには常にリストの最新の登録された変数から探していきます。

また、この変数リストは、クロージャーの中からクロージャを呼び出した場合には、そのまま伸びていきます。

つまり、クロージャー同士で同じ変数名を持つ場合には、最も最新の登録されたクロージャ内の変数を使うことになります。

また、現在実行中のクロージャだけではなく、自分を呼び出したクロージャの変数も呼び出せばそのまま使うことができます。 さらに、呼び出されたクロージャをさらに呼び出した上位のクロージャの変数にもアクセスできることに注意してください。

このように変数リストで管理することによって、クロージャ内で使える変数は、クロージャ内で定義されたものだけでなく、呼び出したクロージャやさらにその上位のクロージャの変数も使えるようにしているのです。

クロージャが終了するときには、変数リストから対応する変数が削除されます。 そのため、すでに実行が終了したクロージャの変数にはアクセスできません。しかし、クロージャ自身は残っているので新たに呼び出されれば元の変数値が残っていて再度使うことができます。

このような仕組みによって、クロージャを実現しました。

Closure Basicでは、変数の値そのものは所属するクロージャ内に持ちますが、変数の情報は呼び出し履歴の関係を含めて変数リストで管理しているのです。

5.3.2.3 環境リスト

環境リストは、新たにクロージャが作成されたときにClosure Basicコンパイラにより、それまでの変数リストを保存します。


  旧変数リスト1 → 旧変数リスト2 → ...

クロージャが処理を終えると、Closure Basicコンパイラは環境リストから保存していた変数リストを取り出してその値を変数リストに復旧します。 つまり、実行を終えたクロージャの中で使われていた変数の定義は、クロージャの実行を終えた後には消去されることになります。

ここで、変数リストをまるごと環境リストにクロージャが作成されるごとに保存するのは無駄なように思えるかも知れません。

しかし、デカルト言語ではリスト構造は共通のアトムやリストは(Lisp言語のリストのように)共有されています。 そのため、環境リストに保存されているそれぞれの変数リストは大部分の共通部分は共有されているので実際には無駄にはなっていないカラクリになっています。 変数リストの古い部分については、共有されているのでメモリ量はたいして増えては行かないのです。

5.3.2.4 アドレスリストとアドレス・オフセット

アドレス・オフセットは、そのクロージャのパラメタ(引数)変数をスタックに割り当てるために使います。

パラメタ変数は他の変数と異なり、クロージャが呼び出されたときにスタック上に領域をとり元の古い値が保存されます。

クロージャの実行が終了すると、スタック上から元のパラメタ変数に値が復旧されます。

これは、再帰関数(クロージャ)の実行の場合には、新しく呼び出されたクロージャ内でパラメタ変数の値が変更されても、元のクロージャ内では値が元のままであることを保障するために必要なのでこのような処理を行います。

アドレス・オフセットにはスタックの上に割り当てられたパラメタの総計の最大アドレスを保存しています。 新しくパラメタ変数が割り当てられると、アドレス・オフセットのアドレスから割り当てられます。 その後アドレス・オフセットは割り当てられたパラメタ変数の大きさだけ引き上げられるのです。

アドレスリストには、クロージャが呼び出されたときのアドレス・オフセットが保存されます。 クロージャが呼ばれるたびに、そのときのアドレス・オフセットがリストに保存されていきます。

そして、クロージャが終了した後には、アドレスリスト内のアドレス・オフセットを復旧します。

このようにアドレスリストとアドレス・オフセットの組み合わせによってパラメタ変数の再帰処理(リカーシブ処理)への対応を行います。 これは、パラメタ変数は、他のクロージャ内の変数とは扱いが異なることに注意してください。

通常のクロージャ変数は、何度クロージャが呼び出されても共通のクロージャ内の領域を使います。

しかし、パラメタ変数は異なり、クロージャが呼び出されるたびに元の値を保存して、クロージャの終了とともに元の値に戻します。