После некоторых тестов resent я обнаружил, что моя реализация не может справиться с очень большой рекурсией. Хотя после того, как я провел несколько тестов в Firefox, я обнаружил, что это может быть более распространено, чем я думал изначально. Я считаю, что основная проблема заключается в том, что для моей реализации требуется 3 вызова для вызова функции. Первый вызов выполняется с помощью метода Call
, который гарантирует, что вызов выполняется вызываемому объекту и получает значение любых аргументов, которые являются ссылками. Второй вызов производится по методу Call
, который определен в интерфейсе ICallable
. Этот метод создает новый контекст выполнения и строит выражение лямбда, если оно не было создано. Последний вызов выполняется для лямбда, который объект функции инкапсулирует. Очевидно, что выполнение вызова функции довольно тяжелое, но я уверен, что с небольшой настройкой я могу сделать рекурсию жизнеспособным инструментом при использовании этой реализации.Как я могу улучшить возможности рекурсии моей реализации 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;
}
Предполагаю, что преобразование хвостовой рекурсии в циклы уже не доступно? Таким образом, вы могли бы избежать полного вызова. –
@DrJokepu - Я придерживался идеи использования хвостовой рекурсии в глубине моего разума, но я также ищу предложения о том, как сделать сами звонки менее тяжелыми, как общее улучшение производительности. Также я не верю, что рекурсия хвоста может быть реализована правильно в случаях, когда сложность функции слишком велика. – ChaosPandion
Ну не похоже, что это делает что-то ненужное, попробовали ли вы его запустить с профилировщиком? Я имею в виду, что вызовы функций (в режиме деблокирования) не очень дороги в CLR (к сожалению, второй вызов слишком утолщен, чтобы быть встроенным JIT), поэтому я сомневаюсь, что именно поэтому он тяжелый. Может быть, что-то в Reference.GetValue() или что-то в этом роде? Профилировщик определенно будет очень полезен. –