Дерево выражений: замените выражения типа <variable>+ 1 / <variable>-1 операциями увеличения / уменьшения, соответственно - PullRequest
1 голос
/ 24 октября 2019

Я хотел бы преобразовать () => a - 1 + b + 1 лямбда во что-то вроде () => a-- + b++ с деревом выражений.

Я реализую класс ExpressionTreeTransformer.cs, который наследуется от ExpressionVisitor.cs, и переопределил VisitBinary метод:

protected override Expression VisitBinary(BinaryExpression node)
        {
            if (!TryGetNumberValueFromExpressionNode(node.Left, out var leftNodeValue))
            {
                return base.VisitBinary(node);
            }

            if (!TryGetNumberValueFromExpressionNode(node.Right, out var rightNodeValue) || rightNodeValue != 1)
            {
                return base.VisitBinary(node);
            }

            var resultedExpression = node.NodeType switch
            {
                ExpressionType.Add => Expression.Increment(Expression.Constant(leftNodeValue)),
                ExpressionType.Subtract => Expression.Decrement(Expression.Constant(leftNodeValue)),
                _ => base.VisitBinary(node)
            };

            return resultedExpression;
        }

Это прекрасно работает, если были применены круглые скобки, такие как () => (a - 1) + (b + 1), но не работает, если мы пытаемся () => a - 1 + b + 1

После некоторого исследования я выяснил, что причина в том, какузлы построения дерева выражений. Без скобок шаги выглядят следующим образом:

  1. влево (a - 1) + вправо (b)
  2. влево (результат 1 шага) + вправо (1)

Узлы выражений обрабатываются в цепочке обработчиков:

_expressionHandlers = new MemberExpressionHandler();
_expressionHandlers.SetSuccessor(new ConstantExpressionHandler());

Обработчик переменных:

public class MemberExpressionHandler : AbstractTreeExpressionHandler
    {
        public override bool Handle(Expression expressionNode, out int nodeValue)
        {
            if (expressionNode is MemberExpression memberExpression)
            {
                var constantExpression = memberExpression.Expression as ConstantExpression;
                var field = (FieldInfo)memberExpression.Member;

                if (constantExpression != null)
                {
                    var value = field.GetValue(constantExpression.Value);
                    var isNumber = int.TryParse(value.ToString(), out nodeValue);

                    if (isNumber)
                    {
                        return true;
                    }
                }
            }
            else
            {
                if (_successor != null)
                {
                    return _successor.Handle(expressionNode, out nodeValue);
                }
            }

            nodeValue = 0;

            return false;
        }
    }

Обработчик констант:

public class ConstantExpressionHandler : AbstractTreeExpressionHandler
    {
        public override bool Handle(Expression expressionNode, out int nodeValue)
        {
            var isConstant = expressionNode is ConstantExpression;
            var isNumber = int.TryParse(((ConstantExpression)expressionNode).Value.ToString(), out nodeValue);

            if (isConstant && isNumber)
            {
                return true;
            }

            return false;
        }
    }

Q: Я застрялПожалуйста, поделитесь своим опытом, как правильно решить эту задачу

Ps результаты:

  • С круглыми скобками: () => (Decrement(0) + Increment(1))
  • Без:() => ((Decrement(0) + value(ExpressionTreeModule.Program+<>c__DisplayClass0_0).b) + 1)

1 Ответ

1 голос
/ 25 октября 2019

Я думаю, что вам нужно будет изучить левую сторону, когда вы найдете постоянное прибавление или вычитание 1 и определите, можете ли вы поднять сложение / вычитание, чтобы оно стало приращением / уменьшением. Вот мой пример кода:

public class UnaryConstant1 : ExpressionVisitor {
    protected override Expression VisitBinary(BinaryExpression node) {
        if (node.Right is ConstantExpression c && c.Type.IsNumeric() && (Int32)c.Value == 1) {
            if (node.NodeType == ExpressionType.Add || node.NodeType == ExpressionType.Subtract) {
                if (node.Left is MemberExpression) {
                    if (node.NodeType == ExpressionType.Add)
                        return Expression.Increment(node.Left);
                    else
                        return Expression.Decrement(node.Left);
                }
                else if (node.Left is BinaryExpression left && (left.NodeType == ExpressionType.Add || left.NodeType == ExpressionType.Subtract)) {
                    Expression right;
                    if (node.NodeType == ExpressionType.Add)
                        right = Expression.Increment(left.Right);
                    else
                        right = Expression.Decrement(left.Right);

                    if (left.NodeType == ExpressionType.Add)
                        return Expression.Add(Visit(left.Left), right);
                    else
                        return Expression.Subtract(Visit(left.Left), right);
                }
            }
        }
        return base.VisitBinary(node);
    }
}
...