9
\$\begingroup\$

There is a job interview question, and the source of the question is here.

The solution is pretty simple. We just need to split the input string by + and then by *. Then we compute products in a nested loop and sum up all the products.

public class Main {

    // http://www.careercup.com/question?id=4911380140392448
    public static void main(String[] args) {
        String equation = "1*5*4+8*9+16";
        int result = compute(equation);
        System.out.println(result);
    }

    static int compute(String equation) {
        int result = 0;
        String []byPluses = equation.split("\\+");
        for (String multipl : byPluses) {
            String []byMultipl = multipl.split("\\*");
            int multiplResult = 1;
            for (String operand : byMultipl) {
                multiplResult *= Integer.parseInt(operand);
            }
            result += multiplResult;
        }
        return result;
    }

}

But what if I was asked the following question after solving the initial problem:

OK. What would you do if you needed to support not only + and * but also - and /?

The quick solution I thought up is this:

package careepcup.fb;

public class Main {

    // http://www.careercup.com/question?id=4911380140392448
    public static void main(String[] args) {
        // An equation with +, -, /, *
        String anotherEquation = "1*5*4+8*9+16/8-9"; // 85
        double another = computeAnother(anotherEquation);
        System.out.println(another);
    }

    static double computeAnother(String equation) {
        double result = 0.0;
        String noMinus = equation.replace("-", "+-");
        String[] byPluses = noMinus.split("\\+");

        for (String multipl : byPluses) {
            String[] byMultipl = multipl.split("\\*");
            double multiplResult = 1.0;
            for (String operand : byMultipl) {
                if (operand.contains("/")) {
                    String[] division = operand.split("\\/");
                    double divident = Double.parseDouble(division[0]);
                    for (int i = 1; i < division.length; i++) {
                        divident /= Double.parseDouble(division[i]);
                    }
                    multiplResult *= divident;
                } else {
                    multiplResult *= Double.parseDouble(operand);
                }
            }
            result += multiplResult;
        }

        return result;
    }
}

What do think of my solution with -, +, /, *?

\$\endgroup\$
7
\$\begingroup\$

@tim has covered a bunch of what I was going to add, but here are some additional points:

You should anticipate having whitespace in the input. A "simple" replace-all would suffice:

equation = equation.replaceAll("\\s+", "");

Your solution of converting - to +- is novel, and effective, but it really should have a comment on it explaining that you are going to re-split the String and parse the new - as part of the integer, rather than as an operator. Leaving it as it is required some detective work on how it functions.

Bug: Your system will fail on things like: 1 + 2 * -3 (ignoring the whitespace).

Your inner workings of the * and / is a bit messy, though. A logical progression for your challenge would be to add, say, a % operator.... which would require a complicated change.

Operators of equal precedence should be handled together. treating * specially when compared to / makes it awkward because you have to test all combinations inside the * breakdown....

I would suggest using a smarter split expression, one that splits on the gaps between the operators and the values...:

String[] parts = operand.split("(?=[/*])|(?<=[/*])");

With the above regex, you will get for example:

1/2*773 -> [1, /, 2, *, 773]

The way the regex works is that it looks for two things (the regex is in two parts - using "lookaround" expressions)....

  • (?=[/*]) - A positive zero-width look-ahead - This says: find any gap between two characters where the next character is a / or *.
  • (?<=[/*]) - A positive zero-width look-behind - This says: *find any gap between two characters where the previous character was a / or *.

Put them together with an or condition, it says: split the input on the gaps before and after / or *. Now, you can just initialize the result to index-1, and then loop through the rest.... :

double result = Double.parseDouble(parts[0]);

for (int i = 1; i < parts.length; i += 2) {
    String op = parts[i];
    double val = Double.parseDouble(parts[i+1]);
    switch (op) {
        case "*" :
            result *= val;
            break;
        case "/" :
            result /= val;
            break;
    }
}
return result;
\$\endgroup\$
6
\$\begingroup\$

Approach

It's good that you realized that additional commands can be added (what calculator seriously only needs + and *), and that a programmer should think about how the program can be extended. It's also great that you realized that -x is just +-x. And for a quick solution your approach is fine.

But what if you now want to add ^ next? Would you just add yet another for loop? Sooner or later this would get really hard to read and maintain (it's already not the easiest to read).

I don't want to suggest a completely different approach, but there are many math parsers out there which you could check out for ideas.

One of the things most of them have in common is that they separate the parsing of the input from the actual calculation of the result. Ideally, the interface of your code would be such that adding new operands is as easy as you just passing an operand and it's functionality and precedence to your code.

Misc

  • Declare your methods explicitly as private or public.
  • Your code would probably be easier to read if you at least extracted the division to its own method.

It could look like this:

private static double computeDivision(String operand) {
    if (!operand.contains("/")) {
        return Double.parseDouble(operand);
    }
    String[] division = operand.split("\\/");
    double divident = Double.parseDouble(division[0]);
    for (int i = 1; i < division.length; i++) {
        divident /= Double.parseDouble(division[i]);
    }
    return divident;
}
\$\endgroup\$

protected by Community Jan 30 '18 at 4:44

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.