2
\$\begingroup\$

I saw that interview question online and gave it a try:
Implement a function that gets a string which represents an arithmethic expression: contains only numbers or the the operators: '+', '-', '*', '/'.
The function returns the calculation result of the arithmethic expression which appears in the string.
This need to be done in O(n) run-time (n is the length of the string) and O(1) extra space.

Example:

"1+2*3" -> 7               

I succeed in implementing this (based mainly on some code I found online), but I just have one problem: logic is too complicated. So when reviewing the code, please tell me how can I simplify the logic here.

Here is the code:

class ArithmethicalStatments {

  private static int applyOperator(char operand, int firstNum, int secondNum){
      switch (operand) {
        case '*':
            firstNum *= secondNum;
            break;
        case '/':
            firstNum /= secondNum;
            break;
        case '+':
            firstNum = secondNum;
            break;
        case '-':
            firstNum = -secondNum;
            break;
      }

      return firstNum;
  }

  public static int calc(String str){
    int sum = 0;
    int lastApplyOpRes = 0;
    int lastNum = 0;
    int len = str.length();
    char prevOperator = '+';

    for (int i = 0; i < len; i++) {
        char c = str.charAt(i);

        if (c >= '0' && c <= '9') {
            lastNum = lastNum * 10 + c - '0';
            continue;
        }

        lastApplyOpRes = applyOperator(prevOperator, lastApplyOpRes , lastNum);
        lastNum = 0;

        switch (c) {
            case '+':
            case '-':
                sum += lastApplyOpRes;
                lastApplyOpRes = 0;
                break;
        }

        prevOperator = c;
    }

    lastApplyOpRes = applyOperator(prevOperator, lastApplyOpRes , lastNum);

    return sum + lastApplyOpRes;
  }
}              

In case you want to modify the code- some Junit tests I wrote for your convenience:

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;

class ArithmethicalStatmentsTest {

    @Test
    void testEmpty() {
        assertEquals(0, ArithmethicalStatments.calc(""));
    }

    @Test
    void testOneNum() {
        assertEquals(5, ArithmethicalStatments.calc("5"));
    }

    @Test
    void testPlus() {
        assertEquals(8, ArithmethicalStatments.calc("5+3"));
    }

    @Test
    void testMul() {
        assertEquals(15, ArithmethicalStatments.calc("5*3"));
    }

    @Test
    void testMulPlus() {
        assertEquals(8, ArithmethicalStatments.calc("2*3+2"));
    }

    @Test
    void testPlusMul() {
        assertEquals(11, ArithmethicalStatments.calc("2+3*3"));
    }

    @Test
    void testPlusMulPlus() {
        assertEquals(16, ArithmethicalStatments.calc("2+3+3*3+2"));
    }

    @Test
    void testMinus() {
        assertEquals(2, ArithmethicalStatments.calc("5-3"));
    }

    @Test
    void testDivision() {
        assertEquals(2, ArithmethicalStatments.calc("6/3"));
    }

    @Test
    void testMinusPlus() {
        assertEquals(1, ArithmethicalStatments.calc("2-3+2"));
    }

    @Test
    void testPlusDivide() {
        assertEquals(5, ArithmethicalStatments.calc("3+4/2"));
    }

    @Test
    void testAll() {
        assertEquals(6, ArithmethicalStatments.calc("2-3+3/3+2*3"));
    }

}
\$\endgroup\$
0
\$\begingroup\$

If your logic is too complicated, why don't you try to separate the concerns. You may create a Parser that will contains more well-named method to parse handle the characters in your stream. This one can either produce an object that is evaluated to produce your result or it can call methods on another class to evaluate each expression as soon as they are found (like the DOM vs SAX parsers).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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