问题
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