Мне необходимо написать логику для калькулятора получающего массив из строк в которой чередуются числа и действия
У меня есть математический пример записанный массивом в котором числа и действия чередуются. Необходимо посчитать результат всего примера при этом сохранить приоритет выполнения и деления. Пробовал делать через циклы но у меня при сдвиге массива вылезают 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 шт):
Начнём с того, что:
- Нужно использовать просто String — и в него вводить выражение (прим. "2+2*2")
- Необходимо данную строку перевести в польскую нотацию
- Необходимо спарсить выражение, записанное в польской нотации
Желания разбираться в вашем коде, думаю, не будет ни у кого, так как для этих целей всегда используются другие подходы. Поэтому я вам предоставлю вариант решения такой задачи.
Что такое польская нотация?
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, поэтому дробная часть отбросилась)
Если вам непонятен данный код, то ничего страшного. Когда то я тоже это всё учил и ничего не понимал. Вам следует разобраться в двух вещах:
- Польская нотация (попробуйте найти описание в Интернете как переводить и сделайте это сами ручками на бумажке или блокноте)
- Что такое структура данных Stack и как её можно использовать для парсинга польской нотации (можете посмотреть мой код и анализировать каждую строчку, рано или поздно вы поймете как это делать)
Как только во всем разберётесь можете модернизировать и усложнять данный парсер (у меня, например, нет поддержи double).