3

私はおもちゃの動的言語 (javascript に悩まされている) を作成しています。私の実装は DLR の上にありますが、この問題の解決策はかなり言語/プラットフォームに依存しないと考えました。

再帰関数、または隣り合って存在する相互再帰関数をコンパイルすることに問題はありません。しかし、ネストされた相互再帰関数をコンパイルするのは非常に困難であることが判明しました。

テストに使用している関数の例は次のとおりです

void f(int x) {
 void g(int y) {
  if((x + y) < 100) {
   f(x + y);
  } else {
   print(x + y);
  }
 }
 g(x);
}

これを解決するための解決策はかなり一般的でなければならず (おそらく私が間違っているかもしれません)、DLR に固有のものではないと考えました。どうにかしてgの内部定義を取り除き、それをfの前に定義し、クロージャを維持する必要があると思います。環境。

4

3 に答える 3

3

クロージャは通常、関数ポインタと引数のリストを組み合わせたものとして表されます。最初のステップは、ネストされたすべての関数をグローバル スコープに持ち上げ、その環境からバインドされた変数をパラメーターとして使用することです。これは次と同等です。

void _f(int x) 
{ 
  closure g = closure(_g,x);       
  call(g,x);  
}

void _g(int x, int y) 
{
  ...;
}

「クロージャ」と「コール」のプリミティブがあれば、機能します。静的言語でclosure()は、関連する変数のみを保持します。動的言語でclosure()は、関数が必要とする場合に備えて、コンテキスト スタック全体を利用できるようにしておく必要があります。

于 2010-01-07T15:03:45.620 に答える
1

動的言語を作成していることは知っていますが、非動的言語にも同じ原則が適用されると思います.シンボルテーブルがまだあり、複数のパスを介してソースを処理する必要があります.

コード生成フェーズの前にセマンティック ツリーを作成している場合、これは簡単です。関数の呼び出しは、関数のセマンティック定義を含むオブジェクト (ノード) を指します。または、この関数を (意味論的に) 呼び出すノードです。関数の呼び出しでは関数の内容を知る必要がないため、シンボル テーブル エントリへのポインタだけで問題なく機能します。

最適化 (末尾再帰?) を行っている場合は、このタイプの最適化を分析する前に 2 つのパスを実行する必要があります。このフェーズはセマンティック/字句解析の後に発生するため、これは私が確認したすべてのコンパイラで正常です。

この記事の図は、私が話していることを示すのに問題ないと思います (ただし、2 つの異なる入力言語の余分なビットがあります)。

于 2010-01-07T14:56:43.043 に答える
0

あなたが達成しようとしているのは、yコンビネーターです

http://blogs.msdn.com/wesdyer/archive/2007/02/02/anonymous-recursion-in-c.aspx

yコンビネータとは何ですか?

于 2010-01-07T16:41:27.573 に答える