Calculator Hack - Wayfinding

6 min read Assignment

Hack

Review this code for key data structures. You will be required to enact a Calculator in the classroom.

Here are the guidelines…

  1. Enactment starts with Original Expression, turns into Tokenized Expression, then to RPN, and finishes with Final Result.
  2. Appoint a Caller(s). A caller is a person who prompts the figures (tokens) through the steps of the calculations. There could be a person that is the overall Caller and others that coordinate entrance and exits into Queues or Stacks.
  3. Create two to three Calculation expressions, one simple and one interesting. Assign people to be a Token, aka TermOrOperator. They will need to move through steps of calculation and into Queues or Stacks.
  4. On Calculation, you will need to simulate the compression of two to three TermOrOperator becoming a single TermOrOperator. This process will continue until you obtain a result.
  5. Do not rush through this, you will be judged for clarity and accuracy on representing alogorithm. It would be best if you simulated all the steps, as the main System.out.println shows it.
  6. To be a TermOrOperator, there are phone apps that make big numbers or symbols. You could change your representation on phone after a calculation, making your phone calculate to intermediate step.
  7. Forming teams in class of 9-10 would be best, three teams per class. Work it out. You have a class and a half, finals will be in Office Hours.
  8. Step through code in debugger, this will help you visualize. Code is in Hacks in Spring Portfolio.
  9. There will be a write-up and capture that needs to be a part of this assignment and blog.

Calculator Project/Hack

The intention of this project is to use the RPN technique to calculate an expression. This led to breaking down the expression into objects as follows.

Code Runner Challenge

RPN Calculator

View IPYNB Source
// CODE_RUNNER: RPN Calculator

class Main {

    static class Token {
        private final Character token;
        private final int precedence;
        private final java.util.function.BiFunction<Double, Double, Double> calculation;
        private final int numArgs;

        public Token() { this('0'); }
        public Token(Character token) { this(token, 0, null, 0); }
        public Token(Character token, int precedence, java.util.function.BiFunction<Double, Double, Double> calculation, int numArgs) {
            this.token = token;
            this.precedence = precedence;
            this.calculation = calculation;
            this.numArgs = numArgs;
        }

        public Character getToken() { return token; }
        public int getPrecedence() { return precedence; }
        public int getNumArgs() { return numArgs; }
        public boolean isPrecedent(Token token) { return this.precedence > token.getPrecedence(); }
        public Double calculate(Double operand1, Double operand2) { return this.calculation.apply(operand1, operand2); }
        public String toString() { return this.token.toString(); }
    }

    static class TermOrOperator extends Token {
        private final String value;

        public TermOrOperator(String value) { this.value = value; }
        public TermOrOperator(Character token) { super(token); this.value = null; }
        public TermOrOperator(Character token, int precedence, java.util.function.BiFunction<Double, Double, Double> calculation) {
            super(token, precedence, calculation, 2); this.value = null;
        }
        public TermOrOperator(Character token, int precedence, java.util.function.BiFunction<Double, Double, Double> calculation, int numArgs) {
            super(token, precedence, calculation, numArgs); this.value = null;
        }

        public String getValue() { return value; }
        public String toString() { return this.value == null ? super.toString() : this.value; }
    }

    static class Tokens {
        private java.util.Map<Character, TermOrOperator> map;

        public Tokens() { this.map = new java.util.HashMap<>(); }

        public void put(Character token) { this.map.put(token, new TermOrOperator(token)); }
        public void put(Character token, int precedence, java.util.function.BiFunction<Double, Double, Double> calculation) {
            this.map.put(token, new TermOrOperator(token, precedence, calculation));
        }
        public void put(Character token, int precedence, java.util.function.BiFunction<Double, Double, Double> calculation, int numArgs) {
            this.map.put(token, new TermOrOperator(token, precedence, calculation, numArgs));
        }

        public TermOrOperator get(Character token) { return this.map.get(token); }
        public int getPrecedence(Character token) { return this.map.get(token).getPrecedence(); }
        public boolean contains(Character token) { return this.map.containsKey(token); }
        public String toString() { return this.map.toString(); }
    }

    static class Calculator {
        private final String expression;
        private java.util.ArrayList<TermOrOperator> terms = new java.util.ArrayList<>();
        private java.util.ArrayList<TermOrOperator> rpnTerms = new java.util.ArrayList<>();
        private Tokens operators = new Tokens();
        private Tokens seperators = new Tokens();
        private Double result = 0.0;

        public Calculator(String expression) {
            initOperators();
            initSeperators();
            this.expression = expression;
            this.termTokenizer();
            this.termsToRPN();
            this.rpnToResult();
        }

        private void initOperators() {
            operators.put('*', 3, (a, b) -> a * b);
            operators.put('/', 3, (a, b) -> a / b);
            operators.put('%', 3, (a, b) -> a % b);
            operators.put('+', 4, (a, b) -> a + b);
            operators.put('-', 4, (a, b) -> a - b);
            operators.put('^', 2, (a, b) -> Math.pow(a, b));
            operators.put('√', 1, (a, b) -> Math.sqrt(a), 1);
        }

        private void initSeperators() {
            seperators.put(' ');
            seperators.put('(');
            seperators.put(')');
        }

        private void termTokenizer() {
            int start = 0;
            StringBuilder multiCharTerm = new StringBuilder();
            for (int i = 0; i < this.expression.length(); i++) {
                Character c = this.expression.charAt(i);
                if (operators.contains(c) || seperators.contains(c)) {
                    if (multiCharTerm.length() > 0)
                        this.terms.add(new TermOrOperator(this.expression.substring(start, i)));
                    TermOrOperator t = operators.get(c);
                    if (t == null) t = seperators.get(c);
                    if (t != null && t.getToken() != ' ') this.terms.add(t);
                    start = i + 1;
                    multiCharTerm = new StringBuilder();
                } else { multiCharTerm.append(c); }
            }
            if (multiCharTerm.length() > 0)
                this.terms.add(new TermOrOperator(this.expression.substring(start)));
        }

        private void termsToRPN() {
            java.util.Stack<TermOrOperator> operatorStack = new java.util.Stack<>();
            for (TermOrOperator term : terms) {
                if (term.getToken() == '(') {
                    operatorStack.push(term);
                } else if (term.getToken() == ')') {
                    while (operatorStack.peek() != null && operatorStack.peek().getToken() != '(')
                        rpnTerms.add(operatorStack.pop());
                    operatorStack.pop();
                } else if (operators.contains(term.getToken())) {
                    while (!operatorStack.isEmpty() && operators.contains(operatorStack.peek().getToken())
                            && term.isPrecedent(operatorStack.peek()))
                        rpnTerms.add(operatorStack.pop());
                    operatorStack.push(term);
                } else { this.rpnTerms.add(term); }
            }
            while (!operatorStack.isEmpty()) rpnTerms.add(operatorStack.pop());
        }

        private void rpnToResult() {
            java.util.Stack<Double> calcStack = new java.util.Stack<>();
            for (TermOrOperator term : this.rpnTerms) {
                Double operand1 = 0.0, operand2 = 0.0, result = 0.0;
                if (operators.contains(term.getToken())) {
                    if (term.getNumArgs() == 1) {
                        operand1 = calcStack.pop();
                    } else {
                        operand2 = calcStack.pop();
                        operand1 = calcStack.pop();
                    }
                    result = term.calculate(operand1, operand2);
                    calcStack.push(result);
                } else { calcStack.push(Double.valueOf(term.getValue())); }
            }
            this.result = calcStack.pop();
        }

        public String toString() {
            return "Original expression: " + this.expression + "\n" +
                   "Tokenized expression: " + this.terms.toString() + "\n" +
                   "Reverse Polish Notation: " + this.rpnTerms.toString() + "\n" +
                   "Final result: " + String.format("%.2f", this.result);
        }
    }

    public static void main(String[] args) {
        String[][] testCases = {
            {"100 + 200 * 3",   "Simple Math"},
            {"(100 + 200) * 3", "Parenthesis Math"},
            {"100.2 - 99.3",    "Decimal Math"},
            {"300 % 200",       "Modulo Math"},
            {"300/200",         "Division Math"},
            {"√(3^2 + 4^2)",    "Pythagorean Theorem"}
        };

        for (String[] testCase : testCases) {
            Calculator calc = new Calculator(testCase[0]);
            System.out.println("=== " + testCase[1] + " ===");
            System.out.println(calc);
            System.out.println();
        }
    }
}

Main.main(null);
Lines: 1 Characters: 0
Output
Click "Run" in code control panel to see output ...

Calulator takes an Expression

Ultimately the class Calculator constructs a calculation object for an expression. This calculates the expression and saves the result and each of the intermediate step into instance variables.

Submit Assignment

Click to upload or drag and drop
PDF, ZIP, images, or documents (Max 10MB per file)

Course Timeline