Вычисление значения математического выражения (обучающий пример)
Есть такой калькулятор, как можно этот код уменьшить, также поставить валидацию?
class Logic
{
static public double Calculate(string input)
{
{
string output = GetExpression(input);
double result = Counting(output);
return result;
}
}
static private string GetExpression(string input)
{
{
string output = string.Empty;
Stack<char> operStack = new Stack<char>();
for (int i = 0; i < input.Length; i++)
{
if (IsDelimeter(input[i]))
continue;
if (Char.IsDigit(input[i]))
{
while (!IsDelimeter(input[i]) && !IsOperator(input[i]))
{
output += input[i];
i++;
if (i == input.Length) break;
}
output += " ";
i--;
}
if (IsOperator(input[i]))
{
if (input[i] == '(')
operStack.Push(input[i]);
else if (input[i] == ')')
{
char s = operStack.Pop();
while (s != '(')
{
output += s.ToString() + ' ';
s = operStack.Pop();
}
}
else
{
if (operStack.Count > 0)
if (GetPriority(input[i]) <= GetPriority(operStack.Peek()))
output += operStack.Pop().ToString() + " ";
operStack.Push(char.Parse(input[i].ToString()));
}
}
}
while (operStack.Count > 0)
output += operStack.Pop() + " ";
return output;
}
}
static private double Counting(string input)
{
{
double result = 0; //Результат
Stack<double> temp = new Stack<double>();
for (int i = 0; i < input.Length; i++)
{
if (Char.IsDigit(input[i]))
{
string a = string.Empty;
while (!IsDelimeter(input[i]) && !IsOperator(input[i]))
{
a += input[i];
i++;
if (i == input.Length) break;
}
temp.Push(double.Parse(a));
i--;
}
else if (IsOperator(input[i]))
{
double a = temp.Pop();
double b = temp.Pop();
switch (input[i])
{
case '+': result = b + a; break;
case '-': result = b - a; break;
case '*': result = b * a; break;
case '/': result = b / a; break;
case '^': result = double.Parse(Math.Pow(double.Parse(b.ToString()), double.Parse(a.ToString())).ToString()); break;
}
temp.Push(result);
}
}
return temp.Peek();
}
}
static private bool IsDelimeter(char c)
{
if ((" =".IndexOf(c) != -1))
return true;
return false;
}
static private bool IsOperator(char с)
{
if (("+-/*^()".IndexOf(с) != -1))
return true;
return false;
}
static private byte GetPriority(char s)
{
switch (s)
{
case '(': return 0;
case ')': return 1;
case '+': return 2;
case '-': return 3;
case '*': return 4;
case '/': return 4;
case '^': return 5;
default: return 6;
}
}
}
class Program
{
static void Main(string[] args)
{
while (true)
{
Console.Write("Введите выражение: ");
string text = Console.ReadLine();
if (text.Length == 0) break;
Console.WriteLine(Logic.Calculate(text));
}
}
}
Ответы (1 шт):
Автор решения: aepot
→ Ссылка
На гениальность не претендую, многое сделано "в лоб", но следующий код вроде-как работает без ошибок.
public static class Calculator
{
private const string numberChars = "01234567890.";
private const string operatorChars = "^*/+-";
public static double Calculate(string expression)
{
if (string.IsNullOrEmpty(expression))
throw new ArgumentException("Пустое выражение недопустимо", nameof(expression));
CheckParenthesis(expression);
return EvaluateParenthesis(expression);
}
private static double EvaluateParenthesis(string expression)
{
string planarExpression = expression;
while (planarExpression.Contains('('))
{
int clauseStart = planarExpression.IndexOf('(') + 1;
int clauseEnd = IndexOfRightParenthesis(planarExpression, clauseStart);
string clause = planarExpression.Substring(clauseStart, clauseEnd - clauseStart);
planarExpression = planarExpression.Replace("(" + clause + ")", EvaluateParenthesis(clause).ToString());
}
return Evaluate(planarExpression);
}
private static int IndexOfRightParenthesis(string expression, int start)
{
int c = 1;
for (int i = start; i < expression.Length; i++)
{
switch (expression[i])
{
case '(': c++; break;
case ')': c--; break;
}
if (c == 0) return i;
}
return -1;
}
private static void CheckParenthesis(string expression)
{
int i = 0;
foreach (char c in expression)
{
switch (c)
{
case '(': i++; break;
case ')': i--; break;
}
if (i < 0)
throw new ArgumentException("Не хватает '('", nameof(expression));
}
if (i > 0)
throw new ArgumentException("Не хватает ')'", nameof(expression));
}
private static double Evaluate(string expression)
{
string normalExpression = expression.Replace(" ", "");
List<char> operators = normalExpression.Split(numberChars.ToCharArray(), StringSplitOptions.RemoveEmptyEntries).Select(x => x[0]).ToList();
List<double> numbers = normalExpression.Split(operatorChars.ToCharArray(), StringSplitOptions.RemoveEmptyEntries).Select(x => double.Parse(x)).ToList();
if (operators.Count + 1 != numbers.Count)
throw new ArgumentException($"Неверный синтаксис в выражении '{expression}'", nameof(expression));
foreach (char o in operatorChars)
{
for (int i = 0; i < operators.Count; i++)
{
if (operators[i] == o)
{
double result = Calc(numbers[i], numbers[i + 1], o);
numbers[i] = result;
numbers.RemoveAt(i + 1);
operators.RemoveAt(i);
i--;
}
}
}
return numbers[0];
}
private static double Calc(double left, double right, char oper)
{
switch (oper)
{
case '+': return left + right;
case '-': return left - right;
case '*': return left * right;
case '/': return left / right;
case '^': return Math.Pow(left, right);
default: throw new ArgumentException("Неподдерживаемый оператор", nameof(oper));
}
}
}
Вот так использовать
static void Main(string[] args)
{
// Чтобы дробные числа были с точкой, а не с запятой
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
try
{
double result = Calculator.Calculate("2^2*2^(2+3)*4");
Console.WriteLine(result);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
И вот такой вывод в консоль
512
Я здесь не валидирую все подряд, можно в некоторых случаях бросать более вменяемые исключения, это просто обучающий пример, разберите его.