Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- public class Calculator {
- /**
- * Главный метод для решения, извне доступен только он
- * @param exp полное выражение для решения (со скобками)
- * @return решение
- */
- public static double solve(String exp) {
- // для пустой строки возвращаем 0
- if (exp.isEmpty()) return 0;
- // делаем замены, пока не избавимся от всех скобок
- while (true) {
- // если совпадений нет, то со всеми скобками разобрались
- ParenthesesMatcher matcher = new ParenthesesMatcher(exp);
- if (!matcher.match()) break;
- // рекурсивно вызвать эту же функцию для вложенного выражения
- // рано или поздно доберёмся до выражения без скобок
- double result = solve(matcher.get());
- // заменить выражение в скобках на его решение
- exp = matcher.replaceWith(String.valueOf(result));
- }
- // осталось выражение без скобок, передаём его упрощённому решателю
- return solveBasic(exp);
- }
- /**
- * Упрощённый решатель для составных выражений без скобок
- * Сам ничего не решает, использует вспомогательную функцию solveBasicForRegex
- * @param exp выражение для решения (сколько угодно операций, но без скобок)
- * @return решение
- */
- private static double solveBasic(String exp) {
- // сначала делаем все умножения и деления, потом сложения и вычитания
- String mulDiv = solveBasicForRegex(exp, "[\\d.-]+ *[*/] *[\\d.-]+");
- String addSub = solveBasicForRegex(mulDiv, "[\\d.-]+ *[+\\-] *[\\d.-]+");
- // после этого у нас остаётся чистый ответ, который можно вернуть
- return Double.parseDouble(addSub);
- }
- /**
- * Функция для решения составных выражений без скобок
- * @param exp выражение для решения (сколько угодно операций, но без скобок)
- * @param regex регулярное выражение для поиска операций
- * @return решение
- */
- private static String solveBasicForRegex(String exp, String regex) {
- while (true) {
- // если совпадений нет, то со всеми операциями разобрались
- Matcher m = Pattern.compile(regex).matcher(exp);
- if (!m.find()) break;
- // посчитать результат операции и заменить операцию этим результатом
- double result = solveSimple(m.group());
- exp = exp.substring(0 , m.start()) + result + exp.substring(m.end());
- }
- // остался чистый результат всего выражения, возвращаем
- return exp;
- }
- /**
- * Решатель базовой операции в строковом виде
- * @param exp выражение для решения (операнд, операция, операнд)
- * @return решение
- */
- private static double solveSimple(String exp) {
- // матчер для поиска символа операции
- Matcher m = Pattern.compile("[+\\-*/]").matcher(exp);
- // инициализация операндов
- double op1 = Double.MIN_VALUE, op2 = Double.MIN_VALUE;
- // ищем несколько раз, чтобы не принять отрицательное число за операцию
- while (m.find()) {
- try {
- // всё что слева от операции - операнд 1, всё что справа - операнд 2
- // если мы неправильно выбрали что-то, будет исключение
- op1 = Double.parseDouble(exp.substring(0, m.start()).trim());
- op2 = Double.parseDouble(exp.substring(m.end()).trim());
- break;
- }
- // ничего не делаем, просто продолжаем искать операцию
- catch (NumberFormatException ignored) {}
- }
- // вернуть решение для найденной операции
- return solveElementary(op1, op2, m.group());
- }
- /**
- * Самый базовый решатель
- * @param op1 операнд 1
- * @param op2 операнд 2
- * @param op символ операции
- * @return решение
- */
- private static double solveElementary(double op1, double op2, String op) {
- // ручная проверка по всем доступным операциям
- switch (op) {
- case "+": return op1 + op2;
- case "-": return op1 - op2;
- case "*": return op1 * op2;
- case "/": return op1 / op2;
- default: throw new IllegalArgumentException();
- }
- }
- /**
- * Вспомогательный класс для работы со скобками
- */
- private static class ParenthesesMatcher {
- private int start, end;
- private final String exp;
- /**
- * Инициализация
- * @param exp строка, по которой будет поиск выражения в скобках
- */
- ParenthesesMatcher(String exp) {
- this.exp = exp;
- }
- /**
- * Попытаться найти ближайшее выражение в скобках
- * Заполняет поля start и end, если нашлось
- * @return true - нашлось, false - нет
- */
- public boolean match() {
- // счетчик, показывающий баланс скобок
- // если не равен нулю — каких-то скобок больше, чем других
- Integer counter = null;
- for (int i = 0; i < exp.length(); i++) {
- if (exp.charAt(i) == '(') {
- // если это первая открывающая скобка, то наше выражение начинается здесь
- if (counter == null) {
- start = i;
- counter = 1;
- }
- else counter++;
- }
- else if (exp.charAt(i) == ')') {
- // если мы наткнулись на закрывающую скобку до открывающей, то это фигня какая-то
- if (counter == null) {
- return false;
- }
- // уменьшить счетчик и проверить баланс скобок
- if (--counter == 0) {
- // нашелся конец нашего выражения, задаём конечную границу
- end = i + 1;
- return true;
- }
- }
- }
- // не нашли выражений в скобках
- return false;
- }
- /**
- * Получить найденное выражение
- * @return найденное выражение (без скобок)
- */
- public String get() {
- return exp.substring(start + 1, end - 1);
- }
- /**
- * Заменить найденное выражение на заданную строку
- * @param replacement заменяющая строка
- * @return строка с замененным выражением
- */
- public String replaceWith(String replacement) {
- return exp.substring(0 , start) + replacement + exp.substring(end);
- }
- /**
- * Получить начало выражения
- * @return индекс начала выражения (включительно)
- */
- public int start() {
- return start;
- }
- /**
- * Получить конец выражения
- * @return индекс конца выражения (включительно)
- */
- public int end() {
- return end;
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement