Mathematical Equality of Func<int, int> Part Two: Using the Syntax Tree

时光总嘲笑我的痴心妄想 提交于 2019-12-12 09:56:17

问题


So, for those who have not been involved in my earlier question on this topic, I have some C# types to represent Sequences, like in math, and I store a formula for generating the nth-term as Func NTerm;

Now, I would like to compare one Sequence's nth-term formula to another for mathematical equality. I have determined that practically, this requires a limitation to five simple binary operators: +,-,/,*,%.

With tremendous help from Scott Chamberlain on the OP, I've managed to get here. I'm envisioning some kind of recursive solution, but I'm not quite sure where to proceed. Will keep chugging away. Currently, this handles single binary expressions like x + 5 or 7 * c, well, but doesn't deal with other cases. I think the idea is to start from the end of the List, since I think it parses with last operations at the top of the syntax tree and first ones at the bottom. I haven't yet done extensive testing for this method.

UPDATE

public sealed class LambdaParser : ExpressionVisitor
{
    public List<BinaryExpression> Expressions { get; } = new List<BinaryExpression> ();

    protected override Expression VisitBinary (BinaryExpression node)
    {
        Expressions.Add (node);

        return base.VisitBinary (node);
    }

    public bool MathEquals (LambdaParser g)
    {
        try
        {
            return MathEquals (Expressions, g.Expressions);
        } catch (Exception e)
        {
            throw new Exception ("MathEquals", e);
        }
    }

    public static bool MathEquals (List<BinaryExpression> f, List<BinaryExpression> g)
    {
        //handle simple cases
        if (ReferenceEquals (f, g))
            return true;
        if (f == null || g == null)
            return false;
        if (f.Count == 0 || g.Count == 0)
            return false;

        try
        {
            //handle single one element cases
            if (f.Count == 1 && g.Count == 1)
            {
                return MathEquals (f[0], g[0]);
            }
        } catch (Exception e)
        {
            throw new Exception ("MathEquals", e);
        }

        throw new NotImplementedException ("Math Equals");
    }

    static bool MathEquals (BinaryExpression f, BinaryExpression g)
    {
        if (ReferenceEquals (f, g))
            return true;
        if (f == null || g == null)
            return false;
        if (f.NodeType != g.NodeType)
            return false;

        try
        {
            switch (f.NodeType)
            {
                case ExpressionType.Add:
                case ExpressionType.Multiply:
                    return CompareCommutative (f, g);
                case ExpressionType.Subtract:
                case ExpressionType.Divide:
                case ExpressionType.Modulo:
                    return CompareNonCommutative (f, g);
                default:
                    throw new NotImplementedException ($"Math Equals {nameof(f)}: {f.NodeType}, {nameof(g)}: {g.NodeType}");
            }
        } catch (Exception e)
        {
            throw new Exception ($"MathEquals {nameof(f)}: {f.NodeType}, {nameof(g)}: {g.NodeType}", e);
        }
    }

    static bool IsParam (Expression f)
    {
        return f.NodeType == ExpressionType.Parameter;
    }

    static bool IsConstant (Expression f)
    {
        return f.NodeType == ExpressionType.Constant;
    }

    static bool CompareCommutative (BinaryExpression f, BinaryExpression g)
    {
        bool left, right;
        try
        {
            //parse left f to left g and right g
            left = CompareParamOrConstant (f.Left, g.Left) || CompareParamOrConstant (f.Left, g.Right);
            //parse right f to left g and right g
            right = CompareParamOrConstant (f.Right, g.Left) || CompareParamOrConstant (f.Right, g.Right);

            return left && right;
        } catch (Exception e)
        {
            throw new Exception ($"CompareCommutative {nameof(f)}: {f.NodeType}, {nameof(g)}: {g.NodeType}", e);
        }
    }

    static bool CompareNonCommutative (BinaryExpression f, BinaryExpression g)
    {
        bool left, right;
        try
        {
            //compare f left to g left
            left = CompareParamOrConstant (f.Left, g.Left);

            //compare f right to f right
            right = CompareParamOrConstant (f.Right, g.Right);
        } catch (Exception e)
        {
            throw new Exception ($"CompareNonCommutative {nameof(f)}: {f.NodeType}, {nameof(g)}: {g.NodeType}", e);
        }

        return left && right;
    }

    static bool CompareParamOrConstant (Expression f, Expression g)
    {
        var ParamF = IsParam (f);
        var ConstantF = IsConstant (f);

        if (!(ParamF || ConstantF))
        {
            throw new ArgumentException ($"{nameof(f)} is neither a param or a constant", $"{nameof(f)}");
        }
        var ParamG = IsParam (g);
        var ConstantG = IsConstant (g);
        if (!(ParamG || ConstantG))
        {
            throw new ArgumentException ($"{nameof(g)} is neither a param or a constant", $"{nameof(g)}");
        }

        if (ParamF)
        {
            return ParamG;
        }
        if (ConstantF)
        {
            return ConstantG && (f as ConstantExpression).Value.Equals ((g as ConstantExpression).Value);
        }
    }
}

END_UPDATE

I updated the above code to reflect the changes made (mostly refactoring, but also a slightly different approach) after comments reminded me that I'd ignored the non-commutability of some operators.

How do I extend it for expressions with multiple operators of the five defined above? Instead of just 5 * x, something like 2 * x % 3 - x / 5.

来源:https://stackoverflow.com/questions/31689139/mathematical-equality-of-funcint-int-part-two-using-the-syntax-tree

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!