比如表达式 1+2+7*(12.3-(12+9)/3)-(1.1+6.6)+4*5.2
计算出该结果。
实现思路:
1.查找小括号,然后依次消去所有的小括号,此时表达式没有小括号
2.进行乘除运算
3.最后进行加减运算
新建窗体应用程序CalculateUseStackAndRecursionDemo,
将默认的Form1重命名为FormCalculate,窗体 FormCalculate设计器如下:
新建枚举类文件OperatorSymbol.cs,枚举OperatorSymbol源程序如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CalculateUseStackAndRecursionDemo
{
/// <summary>
/// 运算符号,只考虑 加减乘除
/// </summary>
enum OperatorSymbol : ushort
{
/// <summary>
/// 乘,ASCII为42
/// </summary>
Multiply = '*',
/// <summary>
/// 除,ASCII为47
/// </summary>
Divide = '/',
/// <summary>
/// 加,ASCII为43
/// </summary>
Addition = '+',
/// <summary>
/// 减,ASCII为45
/// </summary>
Subtract = '-'
}
}
新建关键的类文件RecursionCalculateUtil.cs,主要的逻辑和流程代码都在这里。
类RecursionCalculateUtil源程序如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CalculateUseStackAndRecursionDemo
{
/// <summary>
/// 使用递归,计算含有小括号,加减乘除的表达式
/// 1.查找小括号,然后依次消去所有的小括号,此时表达式没有小括号
/// 2.进行乘除运算
/// 3.最后进行加减运算
/// </summary>
class RecursionCalculateUtil
{
/// <summary>
/// 显示计算过程事件
/// </summary>
public static event Action<string> EventDisplayProcess;
/// <summary>
/// 含有小括号的四则运算,先计算小括号,再计算乘除,最后计算加减。
/// 当表达式可以直接转化为浮点数时,递归终止
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
public static string GetExpression(string expression)
{
string subExpression = string.Empty;
//找出第一个 右小括号
int rightParenthesisIndex = expression.IndexOf(')');
//找出与第一个右小括号对应的 左小括号
int leftParenthesisIndex = -1;
if (rightParenthesisIndex >= 0)
{
//存在右小括号,不存在左小括号【LastIndexOf:从指定索引开始,向左(向前)查找多少个】
leftParenthesisIndex = expression.LastIndexOf('(', rightParenthesisIndex, rightParenthesisIndex);
if (leftParenthesisIndex == -1)
{
throw new Exception($"表达式【{expression}】非法,缺少开始左小括号\"(\"");
}
//这里解析左右小括号之间的内容,比如:(3+5*6.2)
string parseContent = expression.Substring(leftParenthesisIndex + 1, rightParenthesisIndex - leftParenthesisIndex - 1);
subExpression = ArithmeticWithoutParenthesis(parseContent);
subExpression = expression.Substring(0, leftParenthesisIndex) + subExpression + expression.Substring(rightParenthesisIndex + 1);
EventDisplayProcess?.Invoke($"已消除一对小括号,源表达式【{expression}】,简化后结果【{subExpression}】");
//继续递归,消除小括号
subExpression = GetExpression(subExpression);
}
else
{
//不存在右小括号,但存在左小括号,则表达式非法
leftParenthesisIndex = expression.IndexOf('(');
if (leftParenthesisIndex >= 0)
{
throw new Exception($"表达式【{expression}】非法,缺少结束右小括号\")\"");
}
//这里解析 没有小括号之间的内容,比如:4-9/4.0
subExpression = ArithmeticWithoutParenthesis(expression);
EventDisplayProcess?.Invoke($"这里进行四则运算,源表达式【{expression}】,简化后结果【{subExpression}】");
}
return subExpression;
}
/// <summary>
/// 【没有小括号的】四则运算,先乘除(Multiply,Divide),再加减(Add,Subtract)
/// </summary>
/// <param name="expression"></param>
public static string ArithmeticWithoutParenthesis(string expression)
{
string resultMultiplyDivide = CalculateMultiplyDivide(expression);
return resultMultiplyDivide;//乘除的结果
}
/// <summary>
/// 四则运算,先计算乘除法,如果没有乘除法,就计算加减法
/// </summary>
/// <param name="expression"></param>
private static string CalculateMultiplyDivide(string expression)
{
int indexMultiply = expression.IndexOf('*');
int indexDivide = expression.IndexOf('/');
string simplifiedExpression = string.Empty;
double finalResult;
if (indexMultiply >= 0 && indexDivide >= 0)
{
//既有乘以,又有除以,则找出第一个乘或除
int minIndex = Math.Min(indexMultiply, indexDivide);
simplifiedExpression = GetSimplifiedExpressionForMultiplyDivide(expression, minIndex, minIndex == indexMultiply ? OperatorSymbol.Multiply : OperatorSymbol.Divide);
}
else if (indexMultiply >= 0)
{
//只有乘以,没有除以
simplifiedExpression = GetSimplifiedExpressionForMultiplyDivide(expression, indexMultiply, OperatorSymbol.Multiply);
}
else if (indexDivide >= 0)
{
//只有除以,没有乘以
simplifiedExpression = GetSimplifiedExpressionForMultiplyDivide(expression, indexDivide, OperatorSymbol.Divide);
}
else
{
//没有乘以,也没有除以,此时只用考虑加减操作,如 3+5-2
simplifiedExpression = CalculateAdditionSubtract(expression);
}
if (double.TryParse(simplifiedExpression, out finalResult))
{
EventDisplayProcess?.Invoke($"已计算出【乘除】最终结果【{simplifiedExpression}】,源表达式【{expression}】");
return simplifiedExpression;
}
if (indexMultiply >= 0 || indexDivide >= 0)
{
//这里,继续递归,计算出乘除的结果,依次消除所有的乘除号
simplifiedExpression = CalculateMultiplyDivide(simplifiedExpression);
}
return simplifiedExpression;
}
/// <summary>
/// 计算只有加法、减法的表达式
/// </summary>
/// <param name="expression"></param>
private static string CalculateAdditionSubtract(string expression)
{
double finalResult;
string simplifiedExpression = string.Empty;
if (double.TryParse(expression, out finalResult))
{
return expression;
}
//没有乘以,也没有除以,此时只用考虑加减操作,如 3+5-2
int indexAdd = expression.IndexOf('+');
int indexSubtract = expression.IndexOf('-');
if (indexAdd >= 0 && indexSubtract >= 0)
{
//同时存在 加号、减号
int minIndex = Math.Min(indexAdd, indexSubtract);
simplifiedExpression = GetSimplifiedExpressionForAddSubtract(expression, minIndex, minIndex == indexAdd ? OperatorSymbol.Addition : OperatorSymbol.Subtract);
}
else if (indexAdd >= 0)
{
//存在加号,不存在减号
simplifiedExpression = GetSimplifiedExpressionForAddSubtract(expression, indexAdd, OperatorSymbol.Addition);
}
else if (indexSubtract >= 0)
{
//不存在加号、存在减号
simplifiedExpression = GetSimplifiedExpressionForAddSubtract(expression, indexSubtract, OperatorSymbol.Subtract);
}
else
{
//不存在加号、也不存在减号
if (double.TryParse(expression, out finalResult))
{
return expression;
}
}
if (indexAdd >= 0 || indexSubtract >= 0)
{
//如果存在加号 或者 减号,继续递归,直到 消除所有的加减号
simplifiedExpression = CalculateAdditionSubtract(simplifiedExpression);
}
return simplifiedExpression;
}
/// <summary>
/// 获取【加减后】简化的表达式
/// </summary>
/// <param name="expression"></param>
/// <param name="minIndex"></param>
/// <param name="operatorSymbol"></param>
/// <returns></returns>
private static string GetSimplifiedExpressionForAddSubtract(string expression, int minIndex, OperatorSymbol operatorSymbol)
{
StringBuilder sb = new StringBuilder();
double left;
string leftString = expression.Substring(0, minIndex);
if (!double.TryParse(leftString, out left))
{
throw new Exception($"无法将字符串【{leftString}】转化为浮点数,请检查表达式");
}
double right;
string rightString = string.Empty;
int firstRightSymbol = expression.IndexOfAny(new char[] { '+', '-' }, minIndex + 1);
if (firstRightSymbol == -1)
{
rightString = expression.Substring(minIndex + 1);
}
else
{
rightString = expression.Substring(minIndex + 1, firstRightSymbol - minIndex - 1);
}
if (!double.TryParse(rightString, out right))
{
throw new Exception($"无法将字符串【{rightString}】转化为浮点数,请检查表达式");
}
double result = 0;
switch (operatorSymbol)
{
case OperatorSymbol.Addition:
result = left + right;
break;
case OperatorSymbol.Subtract:
result = left - right;
break;
}
sb.Append(result);
if (firstRightSymbol >= 0)
{
sb.Append(expression.Substring(firstRightSymbol));
}
string simplifiedExpression = sb.ToString();
EventDisplayProcess?.Invoke($"【加减后】简化后的表达式为【{simplifiedExpression}】,源表达式【{expression}】");
return simplifiedExpression;
}
/// <summary>
/// 获取【乘除后】简化的表达式
/// </summary>
/// <param name="expression">源表达式</param>
/// <param name="minIndex">乘除号所在的最小索引</param>
/// <param name="operatorSymbol">操作符</param>
/// <returns></returns>
private static string GetSimplifiedExpressionForMultiplyDivide(string expression, int minIndex, OperatorSymbol operatorSymbol)
{
StringBuilder sb = new StringBuilder();
//将1+8-2*3 转化为1+8-6 【即 2*3直接计算为6】
int lastLeftSymbol = -1;//找出【乘号左边的】最后一个 加减号
int firstRightSymbol = -1;//找出【乘号右边的】第一个 加减乘除号
//LastIndexOfAny:从当前索引处,向前(左)查找多少个
//倒序查找加号 或 减号,没有就是全部【乘号的前面 已经 没有 乘除了】
lastLeftSymbol = expression.LastIndexOfAny(new char[] { '+', '-' }, minIndex, minIndex);
//IndexOfAny:正序查找任何一个符号,从当前索引处,向后(右)查找多少个
firstRightSymbol = expression.IndexOfAny(new char[] { '+', '-', '*', '/' }, minIndex + 1);
double left;
string leftString = string.Empty;
if (lastLeftSymbol == -1)
{
leftString = expression.Substring(0, minIndex);
}
else
{
leftString = expression.Substring(lastLeftSymbol + 1, minIndex - lastLeftSymbol - 1);
}
if (!double.TryParse(leftString, out left))
{
throw new Exception($"无法将字符串【{leftString}】转化为浮点数,请检查表达式");
}
double right;
string rightString = string.Empty;
if (firstRightSymbol == -1)
{
rightString = expression.Substring(minIndex + 1);
}
else
{
rightString = expression.Substring(minIndex + 1, firstRightSymbol - minIndex - 1);
}
if (!double.TryParse(rightString, out right))
{
throw new Exception($"无法将字符串【{rightString}】转化为浮点数,请检查表达式");
}
double result = 0;
switch (operatorSymbol)
{
case OperatorSymbol.Multiply:
result = left * right;
break;
case OperatorSymbol.Divide:
result = left / right;
break;
}
if (lastLeftSymbol == -1)
{
sb.Append(result);
}
else
{
sb.Append(expression.Substring(0, lastLeftSymbol + 1));
sb.Append(result);
}
if (firstRightSymbol >= 0)
{
sb.Append(expression.Substring(firstRightSymbol));
}
string simplifiedExpression = sb.ToString();
EventDisplayProcess?.Invoke($"【乘除后】简化后的表达式为【{simplifiedExpression}】,源表达式【{expression}】");
return simplifiedExpression;
}
}
}
窗体FormCalculate的主要代码如下(忽略设计器自动生成的代码):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CalculateUseStackAndRecursionDemo
{
public partial class FormCalculate : Form
{
public FormCalculate()
{
InitializeComponent();
RecursionCalculateUtil.EventDisplayProcess += RecursionCalculateUtil_EventDisplayProcess;
}
private void RecursionCalculateUtil_EventDisplayProcess(string message)
{
this.BeginInvoke(new Action(() =>
{
if (rtxbDisplay.TextLength > 40960)
{
rtxbDisplay.Clear();
}
rtxbDisplay.AppendText($"{DateTime.Now.ToString("HH:mm:ss.fff")}--->{message}\n");
rtxbDisplay.ScrollToCaret();
}));
}
private void btnRecursion_Click(object sender, EventArgs e)
{
txbResult.Clear();
rtxbDisplay.Clear();
try
{
string result = RecursionCalculateUtil.GetExpression(rtxbExpression.Text);
txbResult.Text = result;
}
catch (Exception ex)
{
RecursionCalculateUtil_EventDisplayProcess($"出错:{ex.Message}");
MessageBox.Show(ex.Message, "出错");
}
}
private void btnStack_Click(object sender, EventArgs e)
{
}
}
}
程序测试如图: