Creating a "calculator" to evaluate arithmetic expressions in Java - code troubles

rmcnary4231 :

I'm trying to solidify my understanding of stacks and operators by creating a simple calculator to process arithmetic expressions that involve parentheses. I feel like I have code that should work, but it's most certainly not giving me the right output.

Even though I have a method to evaluate each expression, when I try to return the number stack it does not print out any evaluated method but only all numbers inputted by the user. I want to deal with issues in input like mismatched operators or missing parentheses as well.

I attempt to run the code with simple expressions like 9 * 5 or something like (7 * 6) + (9 - 4) and it just returns the last double regardless.

Here's my code so far:

Main method

import java.util.Stack;
import javax.swing.JOptionPane;

public class Calculator {

    // instance variables
    private Stack < Double > nums;
    private Stack < String > ops; 
    String list;

        // constructor
    public Calculator()
    {
        nums = new Stack < Double > ();
        ops = new Stack < String > ();
    }

    // methods

    public static boolean isDouble(String str) {
        try {
            Double.parseDouble(str);
        } catch (NumberFormatException e) {
            return false;
        } catch (NullPointerException e) {
            return false;
        }
        return true;
    }

    public static boolean isValidOp(String str) {
        return (str == "(" || str == ")" || str == "^" || str == "*" || str == "/" || str == "+" || str == "-");
    }

    public int prec(String str) {
        if (str == "(" || str == ")")
            return 4;
        if (str == "^")
            return 3;
        if (str == "*" || str == "/")
            return 2;
        if (str == "+" || str == "-")
            return 1;
        else
            return -1;
    }

    public double applyOperator(double left, String op, double right) {
        if (op == "+") {
            return (left + right);
        }
        if (op == "-") {
            return (left - right);
        }
        if (op == "*") {
            return (left * right);
        }
        if (op == "/") {
            return (left / right);
        }
        if (op == "^") {
            return  Math.pow(left, right);
        } else {
            throw new IllegalArgumentException("Not a valid operator");
        }
    }

    public String evaluate(String str)
    {   
        String [] tokens = str.split(" ");

        for (int i = 0; i < tokens.length; i++)
        {
            if (isDouble(tokens [i]) == true)
            {
                nums.push(Double.parseDouble(tokens [i]));
            }   
            if (tokens [i] == "(")
            {
                ops.push(tokens [i]);
            }
            if (tokens [i] == ")")
            {
                String op1 = ops.pop();
                double num1 = nums.pop();
                double num2 = nums.pop();
                double result = applyOperator(num1,op1,num2);
                nums.add(result);
            }
            if (tokens [i] == "+" || tokens [i] == "-" || tokens [i] == "*" || tokens [i] == "/" || tokens [i] == "^")
            {
                if(ops.isEmpty())
                {
                    ops.push(tokens [i]);
                }
                else if (prec(tokens [i]) > prec(ops.peek()))
                {
                    ops.push(tokens [i]);
                }
                else if (prec(tokens [i]) < prec(ops.peek()) && !ops.isEmpty() && ops.peek() != "(")
                {
                    String ac1 = ops.pop();
                    double res1 = nums.pop();
                    double res2 = nums.pop();
                    double outcome = applyOperator(res1,ac1,res2);
                    nums.add(outcome);
                }   
            }
        }

        while(!ops.isEmpty() && nums.size() > 1)
        {
            String ab = ops.pop();
            double bb = nums.pop();
            double cb = nums.pop();
            double clac = applyOperator(bb,ab,cb);
            nums.add(clac);
        }
        String fix = nums.pop().toString();
        return fix;
    }
}

Tester:

import javax.swing.JOptionPane;

public class AppforCalc {

    public static void main(String [] args)
    {
        Calculator calc = new Calculator();
        String reply = "yes";
        String instructions = "Enter a mathematical expression. Separate everything with spaces";

        while(reply.equalsIgnoreCase("yes"))
        {
            String expression = JOptionPane.showInputDialog(instructions);
            String ans = calc.evaluate(expression);
            reply = JOptionPane.showInputDialog("The solution is " + ans + "Try again?");
        }
    }
}
Rann Lifshitz :

The main reason you algorithm fails is due to using == when trying to check for String equality.

In Java, == is a boolean operator which behaves identically for all operands, and checks the equality of the values of the operands. This means that primitives are checked as one might expect, but Strings, which are objects, will result in comparing the memory references of the two Strings, which will result in true only if the two Strings are actually the same String. This means that String equality checks must be done with the equals method.

There are more issues with the behavior of the calculator (algorithmic ones), but those will be easier to identify and fix after handling the String equality checks. One example of an issue that must be fixed is:

 while(!ops.isEmpty() && nums.size() > 1)
        {
            String ab = ops.pop();
            double bb = nums.pop();
            double cb = nums.pop();
            double clac = applyOperator(bb,ab,cb);
            nums.add(clac);
        }

The operands (bb and cb) are popped from a Stack, therefore they arrive in a reveresed order (when parsed, cb was pushed into the stack before bb). This means that cb is the leftside operand and bb is the rightside operand -> double clac = applyOperator(cb,ab,bb); The same refactoring should be done for all usages of the applyOperand method.

Another issue is the following:

        else if (prec(tokens [i]) < prec(ops.peek()) && !ops.isEmpty() && ops.peek() != "(")
        {
            String ac1 = ops.pop();
            double res1 = nums.pop();
            double res2 = nums.pop();
            double outcome = applyOperator(res1,ac1,res2);
            nums.add(outcome);
        } 

An internal evaluation was made, but the triggering of the evaluation is the discvery of an operand with a lower presendence. The operand should be pushed into the operations stack after the evaluation:

 else if (prec(tokens [i]) < prec(ops.peek()) && !ops.isEmpty() && ops.peek() != "(")
        {
            ...
            ...
            nums.add(outcome); // I highly suggest refactoring this to nums.push due to readability considerations
            ops.push(tokens[i]);
        }

References:

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=166713&siteId=1