Провести лексический анализ строки и сказать является ли она верным арифметическим выражением - C#

Узнай цену своей работы

Формулировка задачи:

Прошу сильно не пинать, т.к. только начал изучать дискретку. Есть некоторый алфавит состоящий из чисел типа double, и следующих операций / * + - ( ) Плюс и минус могут быть как унарными, так и бинарными. На входе строка (System.String) над предложенным алфавитом, нужно 1. Провести лексический анализ строки и сказать является ли она верным арифметическим выражением 2. Вычислить данное арифметическое выражение. Может у кого-то есть реализация подобного конечного автомата (код или блок-схема), если поделитесь буду очень признателен.

Решение задачи: «Провести лексический анализ строки и сказать является ли она верным арифметическим выражением»

textual
Листинг программы
using System;
using System.Collections.Generic;
using System.Text;
 
namespace Math
{
    public static class CalculateExpression
    {
        private static readonly Dictionary<char, byte> _opdict = new Dictionary<char, byte> { { '+', 1 }, { '-', 2 }, { '/', 3 }, { '*', 4 }, { '(', 0 } };
 
        private static readonly HashSet<char> _operators = new HashSet<char> {'+', '-', '/', '*'};
 
        private static double CalculateSimple(double a, double b, char op) //Выполняем нужное вычисление в зависимости от операции
        {
            double result = 0;
            switch (op)
            {
                case '+':
                    result = a + b;
                    break;
                case '-':
                    result = a - b;
                    break;
                case '*':
                    result = a*b;
                    break;
                case '/':
                    result = a/b;
                    break;
            }
            return result;
        }
 
        private static double StackCalc(double a, double b, char op) //Так как из стека достаем в обратном порядке, требуется инвертировать левый и правый операнды
        {
            return CalculateSimple(b, a, op);
        }
 
        private static bool BadBreckets(string s) //Проверяем баланс скобок в строке
        {
            int i = 0;
            foreach (var c in s)
            {
                if (c == '(')
                    i++;
                else if (c == ')')
                    i--;
            }
            return i != 0;
        }
 
        private static string Reverse(string s) //Обращаем строку в обратный порядок
        {
            int length = s.Length;
            var sb = new StringBuilder(s.Length, s.Length);
            for (int i = length - 1; i >= 0; i--)
                sb.Append(s[i]);
            return sb.ToString();
        }
        
        public static string ToRPN(string input) //Для более удобного анализа переводим в ОПН
        {
            string result = "";
            var stack = new Stack<char>();
            foreach (char c in input)
            {
                if (c == '(') 
                    stack.Push(c);
                else if (c == ')')//Если встречаем закрывающую скобку, то вытряхиваем в строку все, до появления открывабщей скобки
                {
                    while (stack.Count > 0)
                    {
                        char a = stack.Pop();
                        if (a == '(') break;
                        result += " " + a;
                    }
                }
                else if (_operators.Contains(c))//Иначе если это оператор, вытряхиваем все операторы с большим приоритетом в строку, после этого засовываемся сами в стек
                {
                    result += " ";
                    while (stack.Count > 0 && _opdict[c] < _opdict[stack.Peek()])
                    {
                        result += stack.Pop() + " ";
                    }
                    stack.Push(c);
                }
                else result += c;  //Если это не скобки и не операторы, то это цифра, добавляем к выходной строке
            }
            while (stack.Count > 0) //Вытряхиваем в строку оставшиеся символы
                result += " " + stack.Pop();
            return result;
        }
 
        public static string ToPN(string input)
        {
            return Reverse(ToRPN(input));
        }
 
        public static double? Calculate(string input) //Вычисляем выражение. Если формат ввода неправильный, возвращаем null
        {
            if (BadBreckets(input) || String.IsNullOrEmpty(input))
                return null;
            var strings = ToRPN(input).Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
            var stack = new Stack<double>(input.Length/2);
            foreach (var s in strings)
            {
                if (_opdict.ContainsKey(s[0])) //Если оператор, то выполняем его над последними двумя элементами стека.
                {
                    if (stack.Count < 2) return null;
                    stack.Push(StackCalc(stack.Pop(), stack.Pop(), s[0]));
                }
                else //Иначе это должно быть число, если вдруг что-то другое, то возвращаем null
                {
                    double a;
                    if (!double.TryParse(s, out a)) return null;
                    stack.Push(a);
                }
            }
            return System.Math.Round(stack.Peek(), 15); //В стеке остается единственный элемент, значение выражения, который возвращаем
        }
    }
}

ИИ поможет Вам:


  • решить любую задачу по программированию
  • объяснить код
  • расставить комментарии в коде
  • и т.д
Попробуйте бесплатно

Оцени полезность:

12   голосов , оценка 3.917 из 5
Похожие ответы