使用传统算法进行表达式求值(数字支持科学表示法,操作符支持加减乘除乘方, 支持括号, 不支持函数)
如果 "^" 后紧跟负数-m (m > 0),则把这个数替换成 "(0-m)"。
例如: "-3^-20 + 30" 处理结果为 "0-3^(0-20)+30"。
二. 中缀表达式到后缀表达式的转换
要把表达式从中缀表达式的形式转换成用后缀表示法表示的等价表达式,必须
了解操作符的优先级和结合性。优先级或者说操作符的强度决定求值顺序;优
先级高的操作符比优先级低的操作符先求值。如果所有操作符优先级一样,那
么求值顺序就取决于它们的结合性。操作符的结合性定义了相同优先级操作符
组合的顺序
Left associativity : A+B+C = (A+B)+C
Right associativity : A^B^C = A^(B^C)
转换过程包括用下面的算法读入中缀表达式的操作数、操作符和括号:
1. 初始化一个空栈,一个空队列。
2. 从左到右读入中缀表达式,解析每一个字符,分离出数字与操作符,括号
(包括开括号与闭括号)。
3. 操作数进入队列。
4. 操作符弹出(pop)并进入队列,直至遇见开括号,优先级较低的操作符或者
同一优先级的右结合符号。把这个操作符压入(push)堆栈。
5. 把开括号压入堆栈。
6. 如果遇到闭括号,在遇见开括号前,弹出所有操作符,然后把它们依次进入
队列。
7. 如果到达输入字符串的末尾,弹出所有操作符并依次进入队列。
8. 队列中的元素依次出队组合起来就是后缀表达式,所以直接使用队列进行下一
步求值即可。
三. 后缀表达式求值
对后缀表达式求值比直接对中缀表达式求值简单。在后缀表达式中,不需要括号,
而且操作符的优先级也不再起作用了。可以用如下算法对后缀表达式求值:
1. 初始化一个空堆栈
2. 从左到右读入后缀表达式
3. 如果字符是一个操作数,把它压入堆栈。
4. 如果字符是个操作符,弹出两个操作数,执行恰当操作,然后把结果压入堆栈。
如果您不能够弹出两个操作数,后缀表达式的语法就不正确。
5. 到后缀表达式末尾,从堆栈中弹出结果。若后缀表达式格式正确,那么堆栈应
该为空。
四. 下面的代码使用C# 描述这一算法
// CalculatorHelper.cs
using System;
using System.Collections.Generic;
public static class CalculatorHelper
{
public static bool Calculate(string infixExpression, out double result,
out string errorExpression) {
result = 0;
errorExpression = string.Empty;
infixExpression = infixExpression.Replace(" ", string.Empty);
if (infixExpression.EndsWith("=")) {
infixExpression = infixExpression.Substring(0, infixExpression.Length - 1);
}
IList<char> validCharList = new List<char>();
for (char ch = '0'; ch <= '9'; ch++) {
validCharList.Add(ch);
}
validCharList.Add('.');
validCharList.Add('e');
validCharList.Add('E');
validCharList.Add('+');
validCharList.Add('-');
validCharList.Add('*');
validCharList.Add('/');
validCharList.Add('^');
validCharList.Add('(');
validCharList.Add(')');
foreach (char ch in infixExpression) {
if (!validCharList.Contains(ch)) {
errorExpression = "表达式包含非法字符";
return false;
}
}
for (int i = 0; i < infixExpression.Length; i++) {
char ch = infixExpression[i];
if (ch == '^') {
int j = i + 1;
if (j < infixExpression.Length) {
char nextChar = infixExpression[j];
if (nextChar == '-') {
infixExpression = infixExpression.Insert(j, "(0");
i += 2;
j += 2;
int k = j + 1;
for (; k < infixExpression.Length; k++) {
if (!char.IsNumber(infixExpression[k]) &&
infixExpression[k] != '.') {
break;
}
}
infixExpression = infixExpression.Insert(k, ")");
}
}
}
}
Queue<string> postfixQueue = GetPostfixQueue(infixExpression);
Stack<double> stackOperand = new Stack<double>();
while (postfixQueue.Count != 0) {
string expression = postfixQueue.Dequeue();
if (IsOperator(expression)) {
if (stackOperand.Count < 2) {
errorExpression = "表达式语法错误";
return false;
}
double operand1 = stackOperand.Pop();
double operand2 = stackOperand.Pop();
double resultTmp;
if (Calculate(operand2, operand1, expression, out resultTmp,
out errorExpression)) {
stackOperand.Push(resultTmp);
} else {
return false;
}
} else {
double resultTmp;
if (double.TryParse(expression, out resultTmp)) {
stackOperand.Push(resultTmp);
} else {
errorExpression = string.Format("无法转换{0} 为数字", resultTmp);
return false;
}
}
}
if (stackOperand.Count == 1) {
result = stackOperand.Pop();
return true;
}
errorExpression = "表达式语法错误";
return false;
}
private static bool Calculate(double operand1, double operand2, string expression,
out double result, out string errorExpression) {
result = 0;
errorExpression = string.Empty;
if (expression == "+") {
result = operand1 + operand2;
} else if (expression == "-") {
result = operand1 - operand2;
} else if (expression == "*") {
result = operand1 * operand2;
} else if (expression == "/") {
if (operand2 == 0) {
errorExpression = "除数不能为零";
return false;
}
result = operand1 / operand2;
} else if (expression == "^") {
result = Math.Pow(operand1, operand2);
}
return true;
}
private static Queue<string> GetPostfixQueue(string infixExpression) {
Queue<string> postfixQueue = new Queue<string>();
Stack<char> stack = new Stack<char>();
if (infixExpression[0] == '-') {
infixExpression = "0" + infixExpression;
}
IList<char> validCharList = new List<char>();
for (char i = '0'; i <= '9'; i++) {
validCharList.Add(i);
}
validCharList.Add('.');
validCharList.Add('e');
validCharList.Add('E');
while (infixExpression != string.Empty) {
char ch = infixExpression[0];
if (validCharList.Contains(ch)) {
string oprand = string.Empty;
while (validCharList.Contains(infixExpression[0])) {
oprand += infixExpression[0].ToString();
infixExpression = infixExpression.Substring(1);
if (infixExpression == string.Empty) {
break;
}
}
postfixQueue.Enqueue(oprand);
} else if (IsOperator(ch.ToString())) {
if (stack.Count > 0) {
char chOperator = stack.Peek();
while (!(chOperator == '(' || ComparePriority(chOperator, ch) < 0 ||
(ComparePriority(chOperator, ch) == 0 && chOperator == '^')) &&
stack.Count > 0) {
char chOther = stack.Pop();
postfixQueue.Enqueue(chOther.ToString());
if (stack.Count < 1) {
break;
}
chOperator = stack.Peek();
}
}
stack.Push(ch);
infixExpression = infixExpression.Substring(1);
} else if (ch == '(') {
stack.Push(ch);
infixExpression = infixExpression.Substring(1);
} else if (ch == ')') {
char chOperator = stack.Pop();
while (chOperator != '(' && stack.Count > 0) {
postfixQueue.Enqueue(chOperator.ToString());
chOperator = stack.Pop();
}
infixExpression = infixExpression.Substring(1);
}
}
while (stack.Count > 0) {
char ch = stack.Pop();
if (ch != '(') {
postfixQueue.Enqueue(ch.ToString());
}
}
return postfixQueue;
}
private static int ComparePriority(char chOperator, char otherOperator) {
IDictionary<char, int> dictionary = new Dictionary<char, int>();
dictionary.Add('+', 0);
dictionary.Add('-', 0);
dictionary.Add('*', 1);
dictionary.Add('/', 1);
dictionary.Add('^', 2);
if (dictionary.ContainsKey(chOperator) &&
dictionary.ContainsKey(otherOperator)) {
return dictionary[chOperator] - dictionary[otherOperator];
}
throw new NotSupportedException(string.Format("Not supported operator: {0}",
otherOperator));
}
private static bool IsOperator(string str) {
IList<string> list = new List<string>();
list.Add("+");
list.Add("-");
list.Add("*");
list.Add("/");
list.Add("^");
return list.Contains(str);
}
}
五. 下面的代码用 WPF (Code 而非 Markup) 描述一个简单的UI.
// Calculator.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
internal class Calculator : Window
{
private Label lblResult;
private TextBox textBox;
[STAThread]
private static void Main() {
Application app = new Application();
app.Run(new Calculator());
}
public Calculator() {
this.Title = "Caculator";
this.MinWidth = 500;
this.FontSize = 14;
this.SizeToContent = SizeToContent.WidthAndHeight;
this.ResizeMode = ResizeMode.CanMinimize;
StackPanel stack = new StackPanel();
this.Content = stack;
Grid grid1 = new Grid();
grid1.Margin = new Thickness(5);
stack.Children.Add(grid1);
for (int i = 0; i < 2; i++) {
RowDefinition rowDef = new RowDefinition();
rowDef.Height = GridLength.Auto;
grid1.RowDefinitions.Add(rowDef);
}
ColumnDefinition colDef = new ColumnDefinition();
colDef.Width = GridLength.Auto;
grid1.ColumnDefinitions.Add(colDef);
colDef = new ColumnDefinition();
colDef.Width = new GridLength(100, GridUnitType.Star);
grid1.ColumnDefinitions.Add(colDef);
string[] strLabels = {
"输入表达式:",
"计算结果:"
};
for (int i = 0; i < strLabels.Length; i++) {
Label lbl = new Label();
lbl.Content = strLabels[i];
lbl.VerticalContentAlignment = VerticalAlignment.Center;
grid1.Children.Add(lbl);
Grid.SetRow(lbl, i);
Grid.SetColumn(lbl, 0);
}
this.textBox = new TextBox();
this.textBox.Margin = new Thickness(5);
grid1.Children.Add(this.textBox);
Grid.SetRow(this.textBox, 0);
Grid.SetColumn(this.textBox, 1);
this.lblResult = new Label();
this.lblResult.VerticalAlignment = VerticalAlignment.Center;
grid1.Children.Add(this.lblResult);
Grid.SetRow(this.lblResult, 1);
Grid.SetColumn(this.lblResult, 1);
Grid grid2 = new Grid();
grid2.HorizontalAlignment = HorizontalAlignment.Right;
grid2.Margin = new Thickness(5);
stack.Children.Add(grid2);
grid2.ColumnDefinitions.Add(new ColumnDefinition());
grid2.ColumnDefinitions.Add(new ColumnDefinition());
Button button = new Button();
button.Width = 90;
button.Height = 25;
button.Margin = new Thickness(5);
button.Content = "计算";
button.HorizontalAlignment = HorizontalAlignment.Center;
button.IsDefault = true;
button.Click += ButtonOnClick;
grid2.Children.Add(button);
button = new Button();
button.Width = 90;
button.Height = 25;
button.Margin = new Thickness(5);
button.Content = "关闭";
button.HorizontalAlignment = HorizontalAlignment.Center;
button.IsCancel = true;
button.Click += delegate {
this.Close();
};
grid2.Children.Add(button);
Grid.SetColumn(button, 1);
this.textBox.Text = "-(4e3 + 20 * (35 + 65) - 4000/2- 2* 10^3)*2";
this.textBox.SelectAll();
this.textBox.Focus();
}
private void ButtonOnClick(object sender, RoutedEventArgs args) {
string infixExpression = this.textBox.Text.Trim();
if (infixExpression != string.Empty) {
double result;
string errorExpression;
if (CalculatorHelper.Calculate(infixExpression, out result,
out errorExpression)) {
this.lblResult.Content = result.ToString();
} else {
this.lblResult.Content = errorExpression;
}
}
}
}
六. 使用如下命令编译以上两个文件(假设系统目录在C:盘, 注意不要换行, 下面的换行用空格替换)
csc /t:winexe
/lib:"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"
/r:PresentationCore.dll;PresentationFramework.dll;WindowsBase.dll
/out:Calculator.exe Calculator.cs Calculatorhelper.cs
运行产生的Caculator.exe, 如下图所示: