Po kilku ponownych testach znalazłem, że moja implementacja nie radzi sobie z bardzo dużą rekurencją. Chociaż po uruchomieniu kilku testów w Firefoksie okazało się, że może to być bardziej powszechne, niż początkowo sądziłem. Uważam, że podstawowym problemem jest to, że moja implementacja wymaga 3 wywołań, aby wykonać wywołanie funkcji. Pierwsze wywołanie dotyczy metody o nazwie Call
, która zapewnia, że wywoływane jest wywołanie obiektu wywoływalnego i otrzymuje wartość dowolnych argumentów, które są odwołaniami. Drugie wywołanie dotyczy metody o nazwie Call
, która jest zdefiniowana w interfejsie ICallable
. Ta metoda tworzy nowy kontekst wykonania i buduje wyrażenie lambda, jeśli nie zostało ono utworzone. Ostateczne wywołanie jest wykonane dla lambda, że obiekt funkcji hermetyzuje. Wyraźne wywołanie funkcji jest dość ciężkie, ale jestem pewien, że przy odrobinie ulepszenia mogę uczynić rekurencję użytecznym narzędziem podczas korzystania z tej implementacji.Jak mogę poprawić możliwości rekursji mojej implementacji 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;
}
Przypuszczam, że przekształcanie rekurencji ogona w pętle jest obecnie poza zakresem? W ten sposób unikniesz całkowitego dzwonienia. –
@DrJokepu - Zachowałem pomysł wykorzystania rekurencji ogona w moim umyśle, ale także szukam sugestii, jak sprawić, by rozmowy były mniej ciężkie jako ogólna poprawa wydajności. Nie wierzę też, że rekurencja ogona może być właściwie zaimplementowana w przypadkach, w których złożoność funkcji jest zbyt duża. – ChaosPandion
Cóż, to nie wygląda na to, że robi coś niepotrzebnego, czy próbowałeś go uruchomić z profilerem? Chodzi mi o to, że wywołania funkcji (w trybie Release) nie są bardzo drogie w CLR (niestety drugie Call jest nieco zbyt grube, aby być podkreślonym przez JIT), więc wątpię, że dlatego jest ciężki. Może coś w Reference.GetValue() czy coś takiego? Profiler byłby zdecydowanie bardzo pomocny. –