Skip to content

Commit

Permalink
#293 - alternative expression for the recursive func to pass the test
Browse files Browse the repository at this point in the history
  • Loading branch information
dadhi committed Mar 4, 2021
1 parent da1ddd4 commit 95d1d39
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 11 deletions.
6 changes: 3 additions & 3 deletions src/FastExpressionCompiler/FastExpressionCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ public void AddNonPassedParam(ParameterExpression expr)

if (NonPassedParameters.Length == 0)
{
NonPassedParameters = new[] { expr };
NonPassedParameters = new[] { expr }; // todo: @perf optimize for a single non passed parameter
return;
}

Expand Down Expand Up @@ -1044,7 +1044,7 @@ private static bool TryCollectBoundConstants(ref ClosureInfo closure, Expression
private static bool TryCollectBoundConstants(ref ClosureInfo closure, Expression expr, IReadOnlyList<PE> paramExprs, bool isNestedLambda,
ref ClosureInfo rootClosure, CompilerFlags flags)
{
var paramCount = paramExprs.Count;
var paramCount = paramExprs.Count; // todo: @perf move closer to usage - don't need it in the most cases
#endif
while (true)
{
Expand All @@ -1070,7 +1070,7 @@ private static bool TryCollectBoundConstants(ref ClosureInfo closure, Expression
{
// if parameter is used BUT is not in passed parameters and not in local variables,
// it means parameter is provided by outer lambda and should be put in closure for current lambda
var p = paramCount - 1;
var p = paramCount - 1; // todo: @perf optimize for the 1 parameter - we don't need the while loop or call the methods
while (p != -1 && !ReferenceEquals(paramExprs.GetParameter(p), expr)) --p;
if (p == -1 && !closure.IsLocalVar(expr))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,65 @@ public int Run()
[Test]
public void Test_Recursive_Expression()
{
var expr = MakeFactorialExpression<int>();
var expr = MakeFactorialExpressionWithTheTrick<int>();

expr.PrintCSharp();
var fs = expr.CompileSys();
fs.PrintIL();
var res = fs(4);

var f = expr.CompileFast(true, CompilerFlags.NoInvocationLambdaInlining);
var f = expr.CompileFast(true);
f.PrintIL();
var res2 = f(4);

// f = expr.CompileFast(true);
// f.PrintIL();
// res2 = f(4);

Assert.AreEqual(res, res2);
}

public Expression<Func<T, T>> MakeFactorialExpressionWithTheTrick<T>()
{
var nParam = Expression.Parameter(typeof(T), "n");
var methodVar = Expression.Variable(typeof(Func<T, T>), "fac");
var methodsVar = Expression.Variable(typeof(Func<T, T>[]), "facs");
var one = Expression.Constant(1, typeof(T));

// This does not work:
// Func<int, int> rec = null;
// Func<int, int> tmp = n => n <= 1 ? 1 : n * rec(n - 1);
// rec = tmp; // setting the closure variable! means that this closure variable is not readonly

// This should work:
// var recs = new Func<int, int>[1];
// Func<int, int> tmp = n => n <= 1 ? 1 : n * recs[0](n - 1);
// recs[0] = tmp; // setting the item inside the closure variable of array type should work because of reference semantic


return Expression.Lambda<Func<T, T>>(
Expression.Block(
new[] { methodsVar, methodVar },
Expression.Assign(methodsVar, Expression.NewArrayBounds(typeof(Func<T, T>), Expression.Constant(1))),
Expression.Assign(
methodVar,
Expression.Lambda<Func<T, T>>(
Expression.Condition(
// ( n <= 1 )
Expression.LessThanOrEqual(nParam, one),
// 1
one,
// n * method( n - 1 )
Expression.Multiply(
// n
nParam,
// method( n - 1 )
Expression.Invoke(
Expression.ArrayIndex(methodsVar, Expression.Constant(0)),
Expression.Subtract(nParam, one)))),
nParam)),
Expression.Assign(Expression.ArrayAccess(methodsVar, Expression.Constant(0)), methodVar),
// return method( n );
Expression.Invoke(methodVar, nParam)),
nParam);
}

//from https://chriscavanagh.wordpress.com/2012/06/18/recursive-methods-in-expression-trees/
public Expression<Func<T, T>> MakeFactorialExpression<T>()
{
Expand Down
4 changes: 2 additions & 2 deletions test/FastExpressionCompiler.TestsRunner/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,8 @@ void Run(Func<int> run, string name = null)
Run(new Issue284_Invalid_Program_after_Coalesce().Run);
Run(new FastExpressionCompiler.LightExpression.IssueTests.Issue284_Invalid_Program_after_Coalesce().Run);

// Run(new Issue293_Recursive_Methods().Run);
// Run(new FastExpressionCompiler.LightExpression.IssueTests.Issue293_Recursive_Methods().Run);
Run(new Issue293_Recursive_Methods().Run);
Run(new FastExpressionCompiler.LightExpression.IssueTests.Issue293_Recursive_Methods().Run);

Console.WriteLine($"============={Environment.NewLine}IssueTests are passing in {sw.ElapsedMilliseconds} ms.");
});
Expand Down

0 comments on commit 95d1d39

Please sign in to comment.