何らかのスタックを使用せずに関数呼び出しのセマンティクスを実装することはできません。ワードゲームをプレイすることしかできません(たとえば、「FILOリターンバッファー」など、別の名前を使用します)。
関数呼び出しのセマンティクスを実装していないもの(継続渡しスタイル、アクターなど)を使用し、その上に関数呼び出しのセマンティクスを構築することができます。しかし、これは、関数が戻るときに制御が渡される場所を追跡するための何らかのデータ構造を追加することを意味し、そのデータ構造はスタックのタイプ(または異なる名前/説明のスタック)になります。
互いに呼び出し可能な多くの関数があると想像してください。実行時に、各関数は、関数の終了時にどこに戻るかを知っている必要があります。first呼び出す場合はsecond次のとおりです。
second returns to somewhere in first
次に、もしsecond通話thirdあなたが持っています:
third returns to somewhere in second
second returns to somewhere in first
次に、もしthird通話fourthあなたが持っています:
fourth returns to somewhere in third
third returns to somewhere in second
second returns to somewhere in first
各関数が呼び出されると、より多くの「戻り先」情報をどこかに保存する必要があります。
関数が戻る場合、その「戻る場所」情報が使用され、不要になります。たとえば、fourthどこかに戻っthirdた場合、「どこに戻るか」情報の量は次のようになります。
third returns to somewhere in second
second returns to somewhere in first
基本的に; 「関数呼び出しのセマンティクス」は次のことを意味します。
- 「返却先」情報が必要です
- 情報の量は、関数が呼び出されると増加し、関数が戻ると減少します
- 格納された「戻る場所」情報の最初の部分は、破棄される「戻る場所」情報の最後の部分になります
これは、FILO / LIFOバッファーまたはスタックを記述します。
ツリーのタイプを使用しようとすると、ツリー内のすべてのノードに複数の子が存在することはありません。注:複数の子を持つノードは、関数が同時に 2つ以上の関数を呼び出す場合にのみ発生する可能性があります。これは、何らかの並行性(スレッド、fork()など)を必要とし、「関数呼び出しセマンティクス」ではありません。ツリー内のすべてのノードに複数の子がない場合。その「ツリー」は、FILO / LIFOバッファーまたはスタックとしてのみ使用されます。また、FILO / LIFOバッファーまたはスタックとしてのみ使用されるため、「ツリー」がスタックであると主張するのは当然です(唯一の違いは、単語ゲームや実装の詳細です)。
「関数呼び出しセマンティクス」を実装するために考えられる他のデータ構造にも同じことが当てはまります-スタックとして使用されます(そして唯一の違いは単語ゲームや実装の詳細です)。「関数呼び出しのセマンティクス」に違反しない限り。注:可能であれば、他のデータ構造の例を提供しますが、少し妥当な他の構造は考えられません。
もちろん、スタックの実装方法は実装の詳細です。メモリの領域(「現在のスタックトップ」を追跡する場所)、ある種のリンクリスト(「リスト内の現在のエントリ」を追跡する場所)、または他の方法。また、ハードウェアにサポートが組み込まれているかどうかは関係ありません。
注:いずれかのプロシージャの呼び出しがいつでもアクティブになる場合があります。次に、「どこに戻るか」情報のために静的にスペースを割り当てることができます。これはまだスタックです(たとえば、FILO / LIFOの方法で使用される静的に割り当てられたエントリのリンクリスト)。
また、「関数呼び出しのセマンティクス」に従わないものがあることに注意してください。これらには、「潜在的に非常に異なるセマンティクス」(たとえば、継続パッシング、アクターモデル)が含まれます。また、並行性(スレッド、ファイバーなど)、setjmp/ longjmp、例外処理などの「関数呼び出しセマンティクス」の一般的な拡張機能も含まれています。