Search code examples
.netoptimizationlambdaexpression-trees

Is there an existing library/technique to reduce a .NET expression tree?


I'm working on translating expression trees and I am wondering if there are existing libraries or techniques applicable to reducing/optimizing operations in the tree.

For instance, I would like to be able to collapse a series of sequential negation operations as follows:

arg => !!!!!!!(getValue(arg))
reduces to arg => !getValue(arg)

... or turn a negation followed by an equality operation into a not-equals operation:

arg => !(getValue(arg) == 3)
reduces to arg => getValue(arg) != 3

... or apply De Morgan's laws to logical expressions in general:

arg => !(getValue(arg) < 3 || getValue(arg) >= 5))
reduces to arg => getValue(arg) >= 3 && getValue(arg) < 5

[I've used lambda expressions in the reduced formats above for brevity.]

I understand that these tools can't apply to all possible evaluation of expression trees, but it seems like they would be of use to the class of expression trees that strictly use logical operations.

Is there an existing reference to building an expression tree evaluator that performs these tasks?


Solution

  • It looks like there is no reusable .NET Framework component to addressing the requirements as posed in the question. However, Andrey Shchekin's tool reference provided a good reference as to how to write the required component.

    Here is a snippet that addresses cancellation of sequential Not() operators, distribution of the Not() operator into a binary expression, and application of De Morgan's laws.

    public class NotCollapser : ExpressionVisitor
    {
        // Incomplete list; others removed for brevity.
        private static readonly IDictionary<ExpressionType, Func<BinaryExpression, BinaryExpression>> NotDistributionMap = 
        {
            { ExpressionType.Equal, e => Expression.MakeBinary(ExpressionType.NotEqual, e.Left, e.Right) },
            { ExpressionType.NotEqual, e => Expression.MakeBinary(ExpressionType.Equal, e.Left, e.Right) },
            { ExpressionType.GreaterThan, e => Expression.MakeBinary(ExpressionType.LessThanOrEqual, e.Left, e.Right) },
            { ExpressionType.AndAlso, e => Expression.MakeBinary(ExpressionType.OrElse, Expression.Not(e.Left), Expression.Not(e.Right)) },
            { ExpressionType.OrElse, e => Expression.MakeBinary(ExpressionType.AndAlso, Expression.Not(e.Left), Expression.Not(e.Right)) }
        };
    
    
        protected override Expression VisitUnary(UnaryExpression expression)
        {
            if (expression.NodeType == ExpressionType.Not)
            {
                if (expression.Operand.NodeType == ExpressionType.Not)
                {
                    return Visit((expression.Operand as UnaryExpression).Operand);
                }
    
                if (NotDistributionMap.ContainsKey(expression.Operand.NodeType))
                {
                    var distribute = NotDistributionMap[expression.Operand.NodeType];
                    return Visit(distribute(expression.Operand as BinaryExpression));
                }
            }
    
            return base.VisitUnary(expression);
        }
    }