表达式求值
1. 验证字符串为表达式
2.分拆表达式为操作符,操作数,括号
确定操作符是二元或一元操作符
3.将操作符序列建立二叉树
4.对二叉树进行求值
叶子节点为操作数,父节点为操作符,递归求值
Test :
[TestMethod()]
public void CalculateTest()
{
BinTree<string> target = new BinTree<string>(); // TODO: Initialize to an appropriate value
string src = "";
double actual;
double a;
src = "-1.1+-2.2+3.3";
a = -1.1+-2.2+3.3;
actual = target.Calculate(src);
Assert.IsTrue(Math.Abs(actual - a) < 0.001);
src = "1+2 * 3 - -(-(3 + 2 - 4 * (2 + 5 + 3)))";
a = 1 + 2 * 3 - -(-(3 + 2 - 4 * (2 + 5 + 3)));
actual = target.Calculate(src);
Assert.IsTrue(Math.Abs(actual - a) < 0.001);
src = "-1.1--2.2";
a = -1.1-(-2.2);
actual = target.Calculate(src);
Assert.IsTrue(Math.Abs(actual - a) < 0.001);
src = "10.999+201*30--400*(5+60)+700.0/3+4%2 ";
//"10.999+201*30--400*(5+60)+700/3+4%2";
a = 10.999 + 201 * 30 - (-400 * (5 + 60)) + 700.0/3 + 4 % 2;
actual = target.Calculate(src);
Assert.IsTrue(Math.Abs(actual - a) < 0.001);
src = "700.0/3+(-1.1)";
a = 700.0/ 3+(-1.1);
actual = target.Calculate(src);
Assert.IsTrue(Math.Abs(actual - a) < 0.001);
src = "";
a = 0;
actual = target.Calculate(src);
Assert.IsTrue(Math.Abs(actual - a) < 0.001);
//not support number type ,treat all item as double
//src = "10.999+201*30--400*(5+60)+7000/3+4%2 ";
//a = 10.999 + 201 * 30 - (-400 * (5 + 60)) + 7000 / 3 + 4 % 2;
//actual = target.Calculate(src);
//Assert.IsTrue(Math.Abs(actual - a) < 0.001);
}
实现
public List<ExpressionItem> SpliteExpression(string src)
{
src = src.Replace(" ", "");
List<ExpressionItem> ret = new List<ExpressionItem>();
StringBuilder sb = new StringBuilder();
StringReader sr = new StringReader(src);
int val = sr.Read();
ExpressionCellType currentType = ExpressionCellType.Unkown;
ExpressionCellType nextType = ExpressionCellType.Unkown;
ExpressionCellType prevType = ExpressionCellType.Unkown;
while (val > 0)
{
char c = (char)val;
sb.Append(c);
currentType = GetExpressionItemType(sb.ToString());
int nextVal = sr.Peek();
char nextChar = (char)nextVal;
string nextString = nextChar.ToString();
nextType = GetExpressionItemType(nextString);
if (IsOperand(sb.ToString() + nextString))
{
nextType = ExpressionCellType.Operand;
}
if (nextType != currentType
||
(
currentType == nextType
&& currentType == ExpressionCellType.Operator
)
)
{
ExpressionItem item = new ExpressionItem();
item.Value = sb.ToString();
item.CellType = currentType;
if (currentType == ExpressionCellType.Operator)
{
item.CombineType = OperatorCombineType.Two;
if (prevType == ExpressionCellType.Unkown
|| prevType == ExpressionCellType.Operator)
{
if (item.Value == "("
|| item.Value == ")"
)
{
item.CombineType = OperatorCombineType.Bracket;
}
else if (item.Value == "-")
{
item.CombineType = OperatorCombineType.One;
}
}
}
prevType = currentType;
ret.Add(item);
sb.Clear();
}
val = sr.Read();
}
return ret;
}
public Int32 GetPriority(ExpressionItem item)
{
Int32 priority = -1;
if (item.CombineType == OperatorCombineType.One)
{
priority += 100;
}
else if (item.CombineType == OperatorCombineType.Two)
{
if ("+-".IndexOf(item.Value) > -1)
{
priority += 5;
}
else if ("*/%".IndexOf(item.Value) > -1)
{
priority += 10;
}
}
return priority;
}
private ExpressionCellType GetExpressionItemType(string p)
{
if (IsOperand(p))
{
return ExpressionCellType.Operand;
}
else if (IsOperator(p))
{
return ExpressionCellType.Operator;
}
else
{
return ExpressionCellType.Unkown;
}
}
public bool IsOperand(string src)
{
double d;
bool result = double.TryParse(src, out d);
return result;
}
public bool IsOperator(string src)
{
if (src == null)
{
return false;
}
if (src.Equals("+", StringComparison.OrdinalIgnoreCase)
|| src.Equals("-", StringComparison.OrdinalIgnoreCase)
|| src.Equals("*", StringComparison.OrdinalIgnoreCase)
|| src.Equals("/", StringComparison.OrdinalIgnoreCase)
|| src.Equals("%", StringComparison.OrdinalIgnoreCase)
|| src.Equals("(", StringComparison.OrdinalIgnoreCase)
|| src.Equals(")", StringComparison.OrdinalIgnoreCase)
// || src.Equals("-(", StringComparison.OrdinalIgnoreCase)
)
{
return true;
}
else
{
return false;
}
}
/// <summary>
/// support unary operator
/// </summary>
/// <param name="expressionString"></param>
/// <returns></returns>
public BinTree<ExpressionItem> Expression2Bintree2(string expressionString)
{
List<ExpressionItem> items = SpliteExpression(expressionString);
if (items == null || items.Count == 0)
{
return null;
}
BinTree<ExpressionItem> result = new BinTree<ExpressionItem>();
BinTreeNode<ExpressionItem> parent = null;
Stack<BinTreeNode<ExpressionItem>> operators = new Stack<BinTreeNode<ExpressionItem>>();
Stack<BinTreeNode<ExpressionItem>> operands = new Stack<BinTreeNode<ExpressionItem>>();
int i = 0;
while (i < items.Count)
{
ExpressionItem item = items[i];
BinTreeNode<ExpressionItem> itemNode = new BinTreeNode<ExpressionItem>();
itemNode.Value = item;
if (item.CellType == ExpressionCellType.Operand)
{
operands.Push(itemNode);
}
else if (item.CellType == ExpressionCellType.Operator)
{
if (item.Value == "("
)
{
operators.Push(itemNode);
}
else if (item.Value == ")")
{
//collapse untill (
//todo
bool notReachPrevBracket = true;
do
{
parent = operators.Pop();
if (parent.Value.CombineType == OperatorCombineType.Two)
{
BinTreeNode<ExpressionItem> oprand2 = operands.Pop();
BinTreeNode<ExpressionItem> oprand1 = operands.Pop();
parent.LeftChild = oprand1;
parent.RightChild = oprand2;
}
else if (parent.Value.CombineType == OperatorCombineType.One)
{
BinTreeNode<ExpressionItem> oprand1 = operands.Pop();
parent.LeftChild = oprand1;
}
operands.Push(parent);
if (operators.Count > 0)
{
BinTreeNode<ExpressionItem> prevNode = operators.Peek();//"("
if (prevNode.Value.Value == "("
)
{
notReachPrevBracket = false;
operators.Pop();
}
}
} while (notReachPrevBracket && operators.Count > 0);
}
else
{
//while
while (operators.Count > 0
&&
(
GetPriority(item) <=
GetPriority(operators.Peek().Value)
)
)
{
parent = operators.Pop();
if (parent.Value.CombineType == OperatorCombineType.Two)
{
BinTreeNode<ExpressionItem> oprand2 = operands.Pop();
BinTreeNode<ExpressionItem> oprand1 = operands.Pop();
parent.LeftChild = oprand1;
parent.RightChild = oprand2;
}
else if (parent.Value.CombineType == OperatorCombineType.One)
{
BinTreeNode<ExpressionItem> oprand1 = operands.Pop();
parent.LeftChild = oprand1;
}
operands.Push(parent);
}
operators.Push(itemNode);
}
}
i++;
}
while (operators.Count > 0)
{
parent = operators.Pop();
if (parent.Value.CombineType == OperatorCombineType.Two)
{
BinTreeNode<ExpressionItem> oprand2 = operands.Pop();
BinTreeNode<ExpressionItem> oprand1 = operands.Pop();
parent.LeftChild = oprand1;
parent.RightChild = oprand2;
}
else if (parent.Value.CombineType == OperatorCombineType.One)
{
BinTreeNode<ExpressionItem> oprand1 = operands.Pop();
parent.LeftChild = oprand1;
}
operands.Push(parent);
}
result.Root = operands.Pop();
return result;
}
public double BintreeNodeCollapseRecursive(BinTreeNode<string> parent)
{
double result = double.MinValue;
if (parent.LeftChild == null
&& parent.RightChild == null
)
{
return double.Parse(parent.Value);
}
else
{
double left = BintreeNodeCollapseRecursive(parent.LeftChild);
double right = BintreeNodeCollapseRecursive(parent.RightChild);
result = Calculate(left, parent.Value, right);
}
return result;
}
public double BintreeNodeCollapseRecursive2(BinTreeNode<ExpressionItem> parent)
{
double result = double.MinValue;
if (parent == null)
{
return double.MinValue;
}
if (parent.LeftChild == null
&& parent.RightChild == null
)
{
return double.Parse(parent.Value.Value);
}
else
{
double left = BintreeNodeCollapseRecursive2(parent.LeftChild);
double right = BintreeNodeCollapseRecursive2(parent.RightChild);
if (parent.Value.CombineType == OperatorCombineType.Two)
{
result = Calculate(left, parent.Value.Value, right);
}
else if (parent.Value.CombineType == OperatorCombineType.One)
{
result = Calculate(left, parent.Value.Value);
}
}
return result;
}
private double Calculate(double left, string operatorString)
{
double result = 0;
switch (operatorString)
{
case "-":
result = -1 * left;
break;
default:
break;
}
return result;
}
private double Calculate(double left, string operatorString, double right)
{
double result = 0;
switch (operatorString)
{
case "+":
result = left + right;
break;
case "-":
result = left - right;
break;
case "*":
result = left * right;
break;
case "/":
result = left / right;
break;
case "%":
result = left % right;
break;
default:
break;
}
return result;
}