Advertisement
lisahaik

NotSoSimpleCalculator

Jun 9th, 2021
939
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 9.05 KB | None | 0 0
  1. import java.util.regex.Matcher;
  2. import java.util.regex.Pattern;
  3.  
  4. public class Calculator {
  5.     /**
  6.      * Главный метод для решения, извне доступен только он
  7.      * @param exp полное выражение для решения (со скобками)
  8.      * @return решение
  9.      */
  10.     public static double solve(String exp) {
  11.         // для пустой строки возвращаем 0
  12.         if (exp.isEmpty()) return 0;
  13.  
  14.         // делаем замены, пока не избавимся от всех скобок
  15.         while (true) {
  16.             // если совпадений нет, то со всеми скобками разобрались
  17.             ParenthesesMatcher matcher = new ParenthesesMatcher(exp);
  18.             if (!matcher.match()) break;
  19.  
  20.             // рекурсивно вызвать эту же функцию для вложенного выражения
  21.             // рано или поздно доберёмся до выражения без скобок
  22.             double result = solve(matcher.get());
  23.  
  24.             // заменить выражение в скобках на его решение
  25.             exp = matcher.replaceWith(String.valueOf(result));
  26.         }
  27.         // осталось выражение без скобок, передаём его упрощённому решателю
  28.         return solveBasic(exp);
  29.     }
  30.  
  31.     /**
  32.      * Упрощённый решатель для составных выражений без скобок
  33.      * Сам ничего не решает, использует вспомогательную функцию solveBasicForRegex
  34.      * @param exp выражение для решения (сколько угодно операций, но без скобок)
  35.      * @return решение
  36.      */
  37.     private static double solveBasic(String exp) {
  38.         // сначала делаем все умножения и деления, потом сложения и вычитания
  39.         String mulDiv = solveBasicForRegex(exp, "[\\d.-]+ *[*/] *[\\d.-]+");
  40.         String addSub = solveBasicForRegex(mulDiv, "[\\d.-]+ *[+\\-] *[\\d.-]+");
  41.  
  42.         // после этого у нас остаётся чистый ответ, который можно вернуть
  43.         return Double.parseDouble(addSub);
  44.     }
  45.  
  46.     /**
  47.      * Функция для решения составных выражений без скобок
  48.      * @param exp выражение для решения (сколько угодно операций, но без скобок)
  49.      * @param regex регулярное выражение для поиска операций
  50.      * @return решение
  51.      */
  52.     private static String solveBasicForRegex(String exp, String regex) {
  53.         while (true) {
  54.             // если совпадений нет, то со всеми операциями разобрались
  55.             Matcher m = Pattern.compile(regex).matcher(exp);
  56.             if (!m.find()) break;
  57.  
  58.             // посчитать результат операции и заменить операцию этим результатом
  59.             double result = solveSimple(m.group());
  60.             exp = exp.substring(0 , m.start()) + result + exp.substring(m.end());
  61.         }
  62.  
  63.         // остался чистый результат всего выражения, возвращаем
  64.         return exp;
  65.     }
  66.  
  67.     /**
  68.      * Решатель базовой операции в строковом виде
  69.      * @param exp выражение для решения (операнд, операция, операнд)
  70.      * @return решение
  71.      */
  72.     private static double solveSimple(String exp) {
  73.         // матчер для поиска символа операции
  74.         Matcher m = Pattern.compile("[+\\-*/]").matcher(exp);
  75.  
  76.         // инициализация операндов
  77.         double op1 = Double.MIN_VALUE, op2 = Double.MIN_VALUE;
  78.  
  79.         // ищем несколько раз, чтобы не принять отрицательное число за операцию
  80.         while (m.find()) {
  81.             try {
  82.                 // всё что слева от операции - операнд 1, всё что справа - операнд 2
  83.                 // если мы неправильно выбрали что-то, будет исключение
  84.                 op1 = Double.parseDouble(exp.substring(0, m.start()).trim());
  85.                 op2 = Double.parseDouble(exp.substring(m.end()).trim());
  86.                 break;
  87.             }
  88.             // ничего не делаем, просто продолжаем искать операцию
  89.             catch (NumberFormatException ignored) {}
  90.         }
  91.         // вернуть решение для найденной операции
  92.         return solveElementary(op1, op2, m.group());
  93.     }
  94.  
  95.     /**
  96.      * Самый базовый решатель
  97.      * @param op1 операнд 1
  98.      * @param op2 операнд 2
  99.      * @param op символ операции
  100.      * @return решение
  101.      */
  102.     private static double solveElementary(double op1, double op2, String op) {
  103.         // ручная проверка по всем доступным операциям
  104.         switch (op) {
  105.             case "+": return op1 + op2;
  106.             case "-": return op1 - op2;
  107.             case "*": return op1 * op2;
  108.             case "/": return op1 / op2;
  109.             default: throw new IllegalArgumentException();
  110.         }
  111.     }
  112.  
  113.     /**
  114.      * Вспомогательный класс для работы со скобками
  115.      */
  116.     private static class ParenthesesMatcher {
  117.         private int start, end;
  118.         private final String exp;
  119.  
  120.         /**
  121.          * Инициализация
  122.          * @param exp строка, по которой будет поиск выражения в скобках
  123.          */
  124.         ParenthesesMatcher(String exp) {
  125.             this.exp = exp;
  126.         }
  127.  
  128.         /**
  129.          * Попытаться найти ближайшее выражение в скобках
  130.          * Заполняет поля start и end, если нашлось
  131.          * @return true - нашлось, false - нет
  132.          */
  133.         public boolean match() {
  134.             // счетчик, показывающий баланс скобок
  135.             // если не равен нулю — каких-то скобок больше, чем других
  136.             Integer counter = null;
  137.  
  138.             for (int i = 0; i < exp.length(); i++) {
  139.                 if (exp.charAt(i) == '(') {
  140.                     // если это первая открывающая скобка, то наше выражение начинается здесь
  141.                     if (counter == null) {
  142.                         start = i;
  143.                         counter = 1;
  144.                     }
  145.                     else counter++;
  146.                 }
  147.                 else if (exp.charAt(i) == ')') {
  148.                     // если мы наткнулись на закрывающую скобку до открывающей, то это фигня какая-то
  149.                     if (counter == null) {
  150.                         return false;
  151.                     }
  152.                     // уменьшить счетчик и проверить баланс скобок
  153.                     if (--counter == 0) {
  154.                         // нашелся конец нашего выражения, задаём конечную границу
  155.                         end = i + 1;
  156.                         return true;
  157.                     }
  158.                 }
  159.             }
  160.             // не нашли выражений в скобках
  161.             return false;
  162.         }
  163.  
  164.         /**
  165.          * Получить найденное выражение
  166.          * @return найденное выражение (без скобок)
  167.          */
  168.         public String get() {
  169.             return exp.substring(start + 1, end - 1);
  170.         }
  171.  
  172.         /**
  173.          * Заменить найденное выражение на заданную строку
  174.          * @param replacement заменяющая строка
  175.          * @return строка с замененным выражением
  176.          */
  177.         public String replaceWith(String replacement) {
  178.             return exp.substring(0 , start) + replacement + exp.substring(end);
  179.         }
  180.  
  181.         /**
  182.          * Получить начало выражения
  183.          * @return индекс начала выражения (включительно)
  184.          */
  185.         public int start() {
  186.             return start;
  187.         }
  188.  
  189.         /**
  190.          * Получить конец выражения
  191.          * @return индекс конца выражения (включительно)
  192.          */
  193.         public int end() {
  194.             return end;
  195.         }
  196.     }
  197. }
  198.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement