代码之家  ›  专栏  ›  技术社区  ›  ChaosPandion

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

  •  5
  • ChaosPandion  · 技术社区  · 15 年前

    在一些重新发送测试之后,我发现我的实现不能处理很多递归。虽然我在火狐上做了几次测试,但我发现这可能比我最初想象的要普遍。我相信基本的问题是,我的实现需要3个调用来进行函数调用。第一个调用是对名为 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;
    }
    
    1 回复  |  直到 14 年前
        1
  •  2
  •   ChaosPandion    15 年前

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

    我想谢谢你 德鲁克普 激励这个解决方案。

    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;
    }