Мне необходимо написать логику для калькулятора получающего массив из строк в которой чередуются числа и действия

У меня есть математический пример записанный массивом в котором числа и действия чередуются. Необходимо посчитать результат всего примера при этом сохранить приоритет выполнения и деления. Пробовал делать через циклы но у меня при сдвиге массива вылезают IndexOutOfBoundsException ошибки...

        Scanner scan = new Scanner(System.in);
        
        //получаем массив
        String[] numbers = scan.nextLine().split(" ");
        String[] num2 = new String[0];
        int size = numbers.length;
        int i = 0;
        int h;
        for (h=0;h<size;h++){System.out.println(numbers[h]);}
        i = 1;
        //цикл построен так чтобы он работал пока в примере не останется знаков умножения и деления;
        // разность и сумму можно будет добавить таким же циклом но потом
        while (!numbers[0].contains("*") && !numbers[0].contains("/")) {

            //проверяем если это умножение или деление то два соседних элемента умножаем/делим
            if (Objects.equals(numbers[i], "*")){
                //заменяем один (первый) элемент умножения на результат
                numbers[i-1] = Double.toString(Double.valueOf(numbers[i-1]) * Double.valueOf(numbers[i+1]));
                //обнуляем ненужные элементы
                numbers[i] = null;
                numbers[i+1] = null;
                System.out.println(numbers[i-1]+"  "+numbers[i]+"  "+numbers[i+1]);

                //основные ошибки происходят тут; 
                //поидее эти цциклы переносят первый массив в другой без ненужных данных и потом обратно 
                //чтобы еще раз можно было произвести дейстиве
                try {
                    for (h = 0; h < size; h++) {

                        if (h < i) {
                            num2[h] = numbers[h];
                        } else if (h >= i) {
                            num2[h] = numbers[h + 2];
                        }
                    }
                }catch (IndexOutOfBoundsException ignored){}
                try {
                    for (h = 0; h < num2[0].length(); h++) {
                        numbers[h] = num2[h];
                    }
                }catch(IndexOutOfBoundsException ignored){}
            }else if(Objects.equals(numbers[i], "/")){

                //если он не находит на определенном месте нужный знак то переходит на следующее предположительное местонахождения знака
                //по-идее тут он не должен выйти за границу массива, но...
            }else{i=i+2;}
        }
        System.out.println(" numbers[0] "+numbers[0]);
    }```

Ответы (1 шт):

Автор решения: ulxanxv

Начнём с того, что:

  1. Нужно использовать просто String — и в него вводить выражение (прим. "2+2*2")
  2. Необходимо данную строку перевести в польскую нотацию
  3. Необходимо спарсить выражение, записанное в польской нотации

Желания разбираться в вашем коде, думаю, не будет ни у кого, так как для этих целей всегда используются другие подходы. Поэтому я вам предоставлю вариант решения такой задачи.

Что такое польская нотация?

4+5+1/2+(5*(2-1)) — это "обычная" запись математического выражения
45+12/+521-*+ — это и есть постфиксная запись, или иначе Польская нотация

Зачем? Так намного удобнее считать математическое выражение, просто идёшь по строке и как только натыкаешься на операцию, то берёшь 2 предыдущих значения и считаешь.

Как считать? Обычно для этих целей используется структура данных Stack, хотя сколько людей столько и реализаций...

Класс InfixTransfomer — перевод строки в польскую нотацию

import java.util.Stack;

public class InfixTransformer {

    private final Stack<Character> stack = new Stack<>();

    private final String input;
    private final StringBuilder output = new StringBuilder();

    public InfixTransformer(String input) {
        this.input = input;
    }

    public String doTransform() {
        for (int i = 0; i < input.length(); ++i) {
            char ch = input.charAt(i);
            switch (ch) {
                case '+':
                case '-':
                    readOperator(ch, 1);
                    break;
                case '*':
                case '/':
                    readOperator(ch, 2);
                    break;
                case '(':
                    stack.push(ch);
                    break;
                case ')':
                    readParent();
                    break;
                default:
                    output.append(ch);
                    break;
            }
        }

        while (!stack.isEmpty()) {
            output.append(stack.pop());
        }

        return output.toString();
    }

    private void readOperator(char operator, int priority) {
        while (!stack.isEmpty()) {
            char sOperator = stack.pop();
            if (sOperator == '(') {
                stack.push(sOperator);
                break;
            }

            int sPriority;
            if (sOperator == '+' || sOperator == '-') {
                sPriority = 1;
            } else {
                sPriority = 2;
            }

            if (sPriority < priority) {
                stack.push(sOperator);
                break;
            }

            output.append(sOperator);
        }

        stack.push(operator);
    }

    private void readParent() {
        while (!stack.isEmpty()) {
            char chx = stack.pop();
            if (chx != '(') {
                output.append(chx);
            }
        }
    }

}

Класс MathParser — парсер польской нотации

import java.util.Stack;

public class MathParser {

    private final Stack<Integer> stack = new Stack<>();

    private final String input;

    public MathParser(String input) {
        this.input = input;
    }

    public int doParse() {
        char ch;
        int firstOperand, secondOperand, result;

        for (int i = 0; i < input.length(); ++i) {
            ch = input.charAt(i);
            if (Character.isDigit(ch)) {
                stack.push(ch - '0');
            } else {
                secondOperand = stack.pop();
                firstOperand = stack.pop();
                switch (ch) {
                    case '+':
                        result = firstOperand + secondOperand;
                        break;
                    case '-':
                        result = firstOperand - secondOperand;
                        break;
                    case '*':
                        result = firstOperand * secondOperand;
                        break;
                    case '/':
                        result = firstOperand / secondOperand;
                        break;
                    default:
                        result = 0;
                }

                stack.push(result);
            }
        }

        result = stack.pop();
        return result;
    }

}

Использование

final String input =
        new InfixTransformer("4+5+1/2+(5*(2-1))").doTransform();
System.out.println("Input: " + input); // 45+12/+521-*+

final int result =
        new MathParser(input).doParse();
System.out.println("Result: " + result); // 14 (ответ должен быть 14.5, но просто у меня для result используется int, поэтому дробная часть отбросилась)

Если вам непонятен данный код, то ничего страшного. Когда то я тоже это всё учил и ничего не понимал. Вам следует разобраться в двух вещах:

  1. Польская нотация (попробуйте найти описание в Интернете как переводить и сделайте это сами ручками на бумажке или блокноте)
  2. Что такое структура данных Stack и как её можно использовать для парсинга польской нотации (можете посмотреть мой код и анализировать каждую строчку, рано или поздно вы поймете как это делать)

Как только во всем разберётесь можете модернизировать и усложнять данный парсер (у меня, например, нет поддержи double).

→ Ссылка