PLACE
spec
solutions
Java

RPN Calculator in Java

There is no notion of a function in Java other than being a method of a class, and a class has to be created explicitly. So in this implementation we do create one and put the functions we need inside it. The main function repetitively reads lines from the standard input, breaks each one into a sequence of tokens and passes a non-empty sequence to evalrpn for evaluating the RPN expression. It is also responsible for catching the exceptions caused by the possible syntactic errors and printing an error message when such occur. main has to be so named in order to ensure that the compiled class can be called, i.e. to provide a starting point for the program.

In order to read text lines of unspecified and unrestricted length, in Java it is necessary to go through creating three objects of gradually more specialized classes. Thus we arrive at the needed kind of reader – the object in. This is somewhat clunky, but considered idiomatic and is unavoidable in the language.

A line s is tokenized by applying the split method with a regex defining blanks as delimiters. The resulting sequence of tokens is an array on which a fixed-size list is built using the method Arrays.asList (the entries of the array are reused as list entries). Finally, through addAll, the list is used to initialize the contents of the stack tks. The tokens get pushed in tks in the order of their appearance in the list, so as they can be popped off in reverse order. This is needed (and that is why we build a stack) because evalrpn works recursively from the topmost (originally last) token to the bottommost (first entered) one. Note that both asList and addAll are necessary. The former is the only means to create a ‘collection’ object from an array, and with the latter that collection is reshaped to a more useful one.

trim is applied to the string before splitting it so as to remove the leading blanks if there are any; split alone would have produced an empty-string token in their place, which would therefore go to the bottom of the stack. When s is empty (or all-blank), the resulting token sequence has a single item which is an empty string (instead of being itself an empty sequence, which would have been a more logical outcome), so it is this empty string that we check to see if an input line was empty and skip further processing in this case. Again, in this part the program is unavoidably (though not much) complicated and obscure.

evalrpn tries to parse a token tk as a number and upon success it returns that number. Failing that, an exception is raised which is handled by checking if tk is an operator. If it is not, or if any of the two recursive calls for finding the values of the operator's arguments fails, evalrpn is exited with raising an exception. Note that an exception is raised implicitly if an attempt is made to pop a tk off an empty stack.

An exception raised (and not handled) within evalrpn finally reaches main where it is treated as signaling a syntax error and leads to printing an error message. An exception is also raised within main itself if there are tokens in tks left unused after a call to evalrpn is complete.

import java.io.*;
import java.util.*;

public class Rpn {
  public static void main(String[] args) throws IOException {
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    for (;;) {
      String s = in.readLine();
      if (s==null) break;
      Stack<String> tks = new Stack<String>();
      tks.addAll(Arrays.asList(s.trim().split("[ \t]+")));
      if (tks.peek().equals("")) continue;
      try {
        double r = evalrpn(tks);
        if (!tks.empty()) throw new Exception();
        System.out.println(r);
      }
      catch (Exception e) {System.out.println("error");}
    }
  }

  private static double evalrpn(Stack<String> tks) throws Exception {
    String tk = tks.pop();
    double x,y;
    try {x = Double.parseDouble(tk);}
    catch (Exception e) {
      y = evalrpn(tks);  x = evalrpn(tks);
      if      (tk.equals("+"))  x += y;
      else if (tk.equals("-"))  x -= y;
      else if (tk.equals("*"))  x *= y;
      else if (tk.equals("/"))  x /= y;
      else throw new Exception();
    }
    return x;
  }
}

boykobbatgmaildotcom