如何改进ECMAScript实现的递归功能?

Cha*_*ion 5 c# optimization recursion runtime ecma262

经过一些反复测试后,我发现我的实现无法处理非常多的递归.虽然我在Firefox中运行了一些测试后发现这可能比我原先想象的更常见.我认为基本问题是我的实现需要3次调用来进行函数调用.第一次调用是一个名为的方法Call,它确保调用可调用对象并获取任何引用参数的值.第二次调用是在一个名为的方法Call中定义的ICallable接口.此方法创建新的执行上下文,并在尚未创建lambda表达式的情况下构建它.最后调用是函数对象封装的lambda.显然,进行函数调用非常繁重,但我确信通过一些调整,我可以在使用此实现时使递归成为可行的工具.

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;
}
Run Code Online (Sandbox Code Playgroud)

Cha*_*ion 2

我简直不敢相信这工作起来有多么容易。基本上在我的编译器中,我检查函数是否返回调用自身的结果。如果是这样,我将返回正在传递的参数。然后我只需获取任何参考值并重新调用支持 lambda。有了这个,我就能够进行数百万次递归调用。

我要感谢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;
}
Run Code Online (Sandbox Code Playgroud)