5

いくつかの再送テストの後、私の実装ではあまり多くの再帰を処理できないことがわかりました。Firefox でいくつかのテストを実行した後、これは当初考えていたよりも一般的である可能性があることがわかりました。基本的な問題は、私の実装では、関数呼び出しを行うために 3 つの呼び出しが必要であることだと思います。最初の呼び出しは、名前付きのメソッドに対してCall行われ、呼び出しが呼び出し可能なオブジェクトに対して行われていることを確認し、参照であるすべての引数の値を取得します。2 番目の呼び出しはCallICallableインターフェース。このメソッドは、新しい実行コンテキストを作成し、ラムダ式が作成されていない場合はビルドします。最後の呼び出しは、関数オブジェクトがカプセル化するラムダに対して行われます。関数呼び出しを行うのは明らかにかなり重いですが、この実装を使用する場合、少し調整するだけで再帰を実行可能なツールにできると確信しています。

public static object Call(ExecutionContext context, object value, object[] args)
{
    var func = Reference.GetValue(value) as ICallable;
    if (func == null)
    {
        throw new TypeException();
    }
    if (args != null && args.Length > 0)
    {
        for (int i = 0; i < args.Length; i++)
        {
            args[i] = Reference.GetValue(args[i]);
        }
    }
    var reference = value as Reference;
    if (reference != null)
    {
        if (reference.IsProperty)
        {
            return func.Call(reference.Value, args);
        }
        else
        {
            return func.Call(((EnviromentRecord)reference.Value).ImplicitThisValue(), args);
        }
    }
    return func.Call(Undefined.Value, args);
}

public object Call(object thisObject, object[] arguments)
{
    var lexicalEnviroment = Scope.NewDeclarativeEnviroment();
    var variableEnviroment = Scope.NewDeclarativeEnviroment();
    var thisBinding = thisObject ?? Engine.GlobalEnviroment.GlobalObject;
    var newContext = new ExecutionContext(Engine, lexicalEnviroment, variableEnviroment, thisBinding);
    Engine.EnterContext(newContext);
    var result = Function.Value(newContext, arguments);
    Engine.LeaveContext();
    return result;
}
4

1 に答える 1

2

これがどれほど簡単に機能するか信じられません。基本的に、私のコンパイラでは、関数が自分自身を呼び出した結果を返すかどうかを確認します。その場合は、代わりに渡された引数を返します。次に、参照値を取得して、バッキング ラムダを再度呼び出します。これにより、何百万もの再帰呼び出しを行うことができました。

このソリューションに刺激を与えてくれたDrJokepuに感謝します。

public object Call(object thisObject, object[] arguments)
{
    var lexicalEnviroment = Scope.NewDeclarativeEnviroment();
    var variableEnviroment = Scope.NewDeclarativeEnviroment();
    var thisBinding = thisObject ?? Engine.GlobalEnviroment.GlobalObject;
    var newContext = new ExecutionContext(Engine, lexicalEnviroment, variableEnviroment, thisBinding);
    var result = default(object);
    var callArgs = default(object[]);

    Engine.EnterContext(newContext);
    while (true)
    {
        result = Function.Value(newContext, arguments);
        callArgs = result as object[];
        if (callArgs == null)
        {
            break;
        }
        for (int i = 0; i < callArgs.Length; i++)
        {
            callArgs[i] = Reference.GetValue(callArgs[i]);
        }
        arguments = callArgs;
    }
    Engine.LeaveContext();

    return result;
}
于 2010-05-24T01:27:49.010 に答える