2010-05-23 63 views
5

经过一些resent测试后,我发现我的实现不能处理非常多的递归。尽管在Firefox中进行了一些测试后,我发现这可能比我原先想象的更普遍。我相信基本的问题是我的实现需要3次调用才能进行函数调用。第一次调用是一个名为Call的方法,它确保调用正在对可调用对象进行调用,并获取任何引用参数的值。第二次调用是在ICallable接口中定义的名为Call的方法。此方法创建新的执行上下文,并在未创建的情况下构建lambda表达式。最后的调用是对函数对象封装的lambda进行的。清楚地做一个函数调用是相当繁重的,但我相信只要稍微调整一下,我就可以在使用这个实现时使递归成为可行的工具。我该如何改进ECMAScript实现的递归功能?

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

我想转换尾循环到循环现在不在范围之内?这样你可以避免完全调用。 – 2010-05-23 22:13:10

+0

@DJJJJSJP - 我始终坚持在我脑海中使用尾递归的想法,但我也在寻找如何使调用本身不那么沉重以提高性能的建议。另外,我不认为在函数的复杂性太大的情况下,尾递归可以正确实现。 – ChaosPandion 2010-05-23 22:34:47

+0

好吧,它看起来并没有做任何不必要的事情,你有没有试过用探查器来运行它?我的意思是,函数调用(在释放模式下)在CLR中并不是非常昂贵(不幸的是,第二个调用有点太不能被JIT内联),所以我怀疑这就是为什么它很重。也许在Reference.GetValue()什么的?分析器肯定会非常有帮助。 – 2010-05-23 23:11:44

回答

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