此程序展示了如何使用 C# 代码来计算数学表达式。该程序以以下代码开始。
此代码声明了一个Dictionary,稍后将使用它来保存变量。(例如,如果用户想要 A = 10、B = 3 和 Pi = 3.14159265。)
然后它定义了一个Precedence枚举来表示运算符的优先级。例如,乘法的优先级高于加法。
单击“Evaluate”按钮时,程序会复制您输入到“ Primatives Dictionary”中的任何基元,然后调用EvaluateExpression方法,该方法会执行所有有趣的工作。该方法很长,因此我将分段描述。
// Stores user-entered primitives like X = 10.
private Dictionary<string, string> Primatives;private enum Precedence
{None = 11,Unary = 10, // Not actually used.Power = 9, // We use ^ to mean exponentiation.Times = 8,Div = 7,Modulus = 6,Plus = 5,
}
// Evaluate the expression.
private double EvaluateExpression(string expression)
{int best_pos = 0;int parens = 0;// Remove all spaces.string expr = expression.Replace(" ", "");int expr_len = expr.Length;if (expr_len == 0) return 0;// If we find + or - now, then it's a unary operator.bool is_unary = true;// So far we have nothing.Precedence best_prec = Precedence.None;// Find the operator with the lowest precedence.// Look for places where there are no open// parentheses.for (int pos = 0; pos < expr_len; pos++){// Examine the next character.string ch = expr.Substring(pos, 1);// Assume we will not find an operator. In// that case, the next operator will not// be unary.bool next_unary = false;if (ch == " "){// Just skip spaces. We keep them here// to make the error messages easier to}else if (ch == "("){// Increase the open parentheses count.parens += 1;// A + or - after "(" is unary.next_unary = true;}else if (ch == ")"){// Decrease the open parentheses count.parens -= 1;// An operator after ")" is not unary.next_unary = false;// if parens < 0, too many )'s.if (parens < 0)throw new FormatException("Too many close parentheses in '" +expression + "'");}else if (parens == 0){// See if this is an operator.if ((ch == "^") || (ch == "*") ||(ch == "/") || (ch == "\\") ||(ch == "%") || (ch == "+") ||(ch == "-")){// An operator after an operator// is unary.next_unary = true;// See if this operator has higher// precedence than the current one.switch (ch){case "^":if (best_prec >= Precedence.Power){best_prec = Precedence.Power;best_pos = pos;}break;case "*":case "/":if (best_prec >= Precedence.Times){best_prec = Precedence.Times;best_pos = pos;}break;case "%":if (best_prec >= Precedence.Modulus){best_prec = Precedence.Modulus;best_pos = pos;}break;case "+":case "-":// Ignore unary operators// for now.if ((!is_unary) &&best_prec >= Precedence.Plus){best_prec = Precedence.Plus;best_pos = pos;}break;} // End switch (ch)} // End if this is an operator.} // else if (parens == 0)is_unary = next_unary;} // for (int pos = 0; pos < expr_len; pos++)
该方法的这一部分用于查找表达式中优先级最低的运算符。为此,它只需循环遍历表达式,检查其运算符字符,并确定它们的优先级是否低于先前找到的运算符。
下面的代码片段显示了下一步。
// If the parentheses count is not zero,// there's a ) missing.if (parens != 0){throw new FormatException("Missing close parenthesis in '" +expression + "'");}// Hopefully we have the operator.if (best_prec < Precedence.None){string lexpr = expr.Substring(0, best_pos);string rexpr = expr.Substring(best_pos + 1);switch (expr.Substring(best_pos, 1)){case "^":return Math.Pow(EvaluateExpression(lexpr),EvaluateExpression(rexpr));case "*":returnEvaluateExpression(lexpr) *EvaluateExpression(rexpr);case "/":returnEvaluateExpression(lexpr) /EvaluateExpression(rexpr);case "%":returnEvaluateExpression(lexpr) %EvaluateExpression(rexpr);case "+":returnEvaluateExpression(lexpr) +EvaluateExpression(rexpr);case "-":returnEvaluateExpression(lexpr) -EvaluateExpression(rexpr);}}
如果括号未闭合,该方法将引发异常。否则,它会使用优先级最低的运算符作为分界点,将表达式拆分成多个部分。然后,它会递归调用自身来评估子表达式,并使用适当的操作来合并结果。
例如,假设表达式为 2 * 3 + 4 * 5。那么优先级最低的运算符是 +。该函数将表达式分解为 2 * 3 和 4 * 5,并递归调用自身来计算这些子表达式的值(得到 6 和 20),然后使用加法将结果合并(得到 26)。
以下代码显示该方法如何处理函数调用。
// if we do not yet have an operator, there// are several possibilities://// 1. expr is (expr2) for some expr2.// 2. expr is -expr2 or +expr2 for some expr2.// 3. expr is Fun(expr2) for a function Fun.// 4. expr is a primitive.// 5. It's a literal like "3.14159".// Look for (expr2).if (expr.StartsWith("(") & expr.EndsWith(")")){// Remove the parentheses.return EvaluateExpression(expr.Substring(1, expr_len - 2));}// Look for -expr2.if (expr.StartsWith("-")){return -EvaluateExpression(expr.Substring(1));}// Look for +expr2.if (expr.StartsWith("+")){return EvaluateExpression(expr.Substring(1));}// Look for Fun(expr2).if (expr_len > 5 & expr.EndsWith(")")){// Find the first (.int paren_pos = expr.IndexOf("(");if (paren_pos > 0){// See what the function is.string lexpr = expr.Substring(0, paren_pos);string rexpr = expr.Substring(paren_pos + 1,expr_len - paren_pos - 2);switch (lexpr.ToLower()){case "sin":return Math.Sin(EvaluateExpression(rexpr));case "cos":return Math.Cos(EvaluateExpression(rexpr));case "tan":return Math.Tan(EvaluateExpression(rexpr));case "sqrt":return Math.Sqrt(EvaluateExpression(rexpr));case "factorial":return Factorial(EvaluateExpression(rexpr));// Add other functions (including// program-defined functions) here.}}}
此代码检查表达式是否以 ( 开头并以 结尾。如果是,则删除这些括号并计算表达式的其余部分。
接下来,代码确定表达式是否以一元 + 或 - 运算符开头。如果是,程序将计算不带运算符的表达式,如果运算符为 -,则对结果取反。
然后,代码会查找Sin、Cos和Factorial等函数。如果找到,它会调用该函数并返回结果。(下载示例以查看Factorial函数。)您可以类似地添加其他函数。
以下代码显示了该方法的其余部分。
// See if it's a primitive.if (Primatives.ContainsKey(expr)){// Return the corresponding value,// converted into a Double.try{// Try to convert the expression into a value.return double.Parse(Primatives[expr]);}catch (Exception){throw new FormatException("Primative '" + expr +"' has value '" +Primatives[expr] +"' which is not a Double.");}}// It must be a literal like "2.71828".try{// Try to convert the expression into a Double.return double.Parse(expr);}catch (Exception){throw new FormatException("Error evaluating '" + expression +"' as a constant.");}
}
如果表达式仍未求值,则它必须是您在文本框中输入的原始值或数值。代码将检查原始字典以查看表达式是否存在。如果值在字典中,则代码获取其值,将其转换为双精度值,然后返回结果。