Как сделать приоритет с несколькими экспонентами, ^, в арифметическом уравнении
Я работаю над программой, которая решает арифметические уравнения. Я столкнулся с проблемой, которая возникает, когда в строке несколько экспоненциальных операторов, программа не может их правильно решить. Примером может быть: 2 ^ 3 ^ 2, правильный ответ - 512, но программа выводит 64. Это потому, что программа делает 2 ^ 3, а затем 8 ^ 2 вместо 3 ^ 2, а затем 2^9. Дайте мне знать, если у вас есть идеи, как изменить мой текущий код или есть что добавить.
import java.text.DecimalFormat;
import java.util.EmptyStackException;
import myUtil.*;
public class PostFixEvaluator extends Asg6
{
public static class SyntaxErrorException extends Exception
{
SyntaxErrorException(String message)
{
super(message);
}
}
private static final String operators = "+-*/^()";
private AStack<Double> operandStack;
private double evaluateOP(char op) throws Exception
{
double rightside = operandStack.pop();
double leftside = operandStack.pop();
double result = 0;
if(op == '+')
{
result = leftside + rightside;
}
else if(op == '-')
{
result = leftside - rightside;
}
else if(op == '*')
{
result = leftside * rightside;
}
else if(op == '/')
{
if(rightside == 0)
{
throw new Exception("Can not divide by 0, the equation is undefined");
}
else
{
result = leftside / rightside;
}
}
else if(op == '^')
{
result = Math.pow(leftside, rightside);
}
return result;
}
private boolean isOperator(char ch)
{
return operators.indexOf(ch) != -1;
}
public double evaluate(String exp) throws Exception
{
operandStack = new AStack<Double>();
String[] tokens = exp.split("\\s+");
try
{
for(String nextToken : tokens)
{
char firstChar = nextToken.charAt(0);
if(Character.isDigit(firstChar))
{
double value = Double.parseDouble(nextToken);
operandStack.push(value);
}
else if (isOperator(firstChar))
{
double result = evaluateOP(firstChar);
operandStack.push(result);
}
else
{
throw new Exception("Invalid character: " + firstChar);
}
}
double answer = operandStack.pop();
if(operandStack.empty())
{
return answer;
}
else
{
throw new Exception("Syntax Error: Stack should be empty");
}
}
catch(EmptyStackException ex)
{
throw new Exception("Syntax Error: The stack is empty");
}
}
}
2 ответа
Я решил бы это с Приоритетами оператора, поскольку вы могли бы хотеть иметь их так или иначе. Для тестирования я немного изменил класс, так что я мог проверить его, и он наверняка не самый эффективный или читаемый, но вы должны понять, как он работает.
import java.text.DecimalFormat;
import java.util.EmptyStackException;
import java.util.*;
public class PostFixEvaluator
{
public static class SyntaxErrorException extends Exception
{
SyntaxErrorException(String message)
{
super(message);
}
}
private static final String operators = "+-*/^()";
private static int[] operatorPriority = {1,1,2,2,3,10,10};
private Stack<Double> operandStack;
private Stack<Character> operatorStack;
private double evaluateOP(char op) throws Exception
{
double rightside = operandStack.pop();
double leftside = operandStack.pop();
double result = 0;
if(op == '+')
{
result = leftside + rightside;
}
else if(op == '-')
{
result = leftside - rightside;
}
else if(op == '*')
{
result = leftside * rightside;
}
else if(op == '/')
{
if(rightside == 0)
{
throw new Exception("Can not divide by 0, the equation is undefined");
}
else
{
result = leftside / rightside;
}
}
else if(op == '^')
{
result = Math.pow(leftside, rightside);
}
return result;
}
private boolean isOperator(char ch)
{
return operators.indexOf(ch) != -1;
}
public double evaluate(String exp) throws Exception
{
operandStack = new Stack<Double>();
operatorStack = new Stack<Character>();
String[] tokens = exp.split("\\s+");
try
{
for(String nextToken : tokens)
{
char firstChar = nextToken.charAt(0);
if(Character.isDigit(firstChar))
{
double value = Double.parseDouble(nextToken);
operandStack.push(value);
}
else if (isOperator(firstChar))
{
// Try to evaluate the operators on the stack
while (!operatorStack.isEmpty())
{
char tmpOperator = operatorStack.pop();
// If Operator has higher Priority than the one before,
// Calculate it first if equal first calculate the second
// operator to get the ^ problem fixed
if (operatorPriority[operators.indexOf(firstChar)] >= operatorPriority[operators.indexOf(tmpOperator)])
{
operatorStack.push(tmpOperator);
// Operand has to be fetched first
break;
}
else
{
double result = evaluateOP(tmpOperator);
operandStack.push(result);
}
}
operatorStack.push(firstChar);
}
else
{
throw new Exception("Invalid character: " + firstChar);
}
}
// Here we need to calculate the operators left on the stack
while (!operatorStack.isEmpty())
{
char tmpOperator = operatorStack.pop();
// Operator Priority has to be descending,
// or the code before is wrong.
double result = evaluateOP(tmpOperator);
operandStack.push(result);
}
double answer = operandStack.pop();
if(operandStack.empty())
{
return answer;
}
else
{
throw new Exception("Syntax Error: Stack should be empty");
}
}
catch(EmptyStackException ex)
{
throw new Exception("Syntax Error: The stack is empty");
}
}
// For testing Only
public static void main(String[] args) throws Exception
{
PostFixEvaluator e = new PostFixEvaluator();
System.out.println(e.evaluate("2 ^ 3 ^ 2"));
}
}
Вы пытаетесь использовать грамматику LL(1) (что анализирует анализатор рекурсивного спуска) для моделирования правоассоциативного оператора (^
). Правоассоциативный оператор требует левой рекурсии, которая не так легко работает с грамматикой LL(1). Вы захотите взглянуть на левый факторинг: http://en.wikipedia.org/wiki/LL_parser