編譯程序或者腳本解釋程序中經常用到表達式的處理,其中單目運算符需要特殊處理:正號+、負號-、按位取反~、邏輯非!;
尤其是單目運算符與二元運算符混雜的情況下,例如正號+、負號-與二元的加+、減-容易混淆;
實際上仔細觀察中綴表達式的字符流時可以發現也是有規律可循的,單目運算符出現無非兩種情況:
(1)表達式開始的位置,或者括號()內部子表達式開始的位置,出現了+或者-,可以判定爲是單目的正號、負號;舉例:
-30+a*b //一開始就出現符號
3+(-20+b*c) //(後一開始就出現符號
!a && b
(2)上一個字符已經是運算符,這時接着又來了一個運算符,第二個運算符可以判定爲單目運算符;舉例:
30*-20+10 //乘號*後原本期望是個值,結果來了個-,這個-可以判定爲是負號
(3) 前一個字符已經是值(例如變量、字面常量、帶括號的值),接下來如果是運算符,這個運算符肯定不是單目運算符;舉例:
(a+b)-c //(a+b)表示一個完整值,之後的-肯定不是單目運算符
a*b-c //b是一個值,之後的-肯定不是單目運算符
根據以上原則,在掃描中綴表達式時,就能夠識別出哪些是單目運算符了,以下給出支持加減乘除、負號及左右括號的表達式處理代碼:
import java.util.Stack; public class Expression { interface IToken { } static class Literal implements IToken { final int value; Literal(int input) { value = input; } } interface Operator extends IToken { int getLevel(); Literal calculate(Literal... literals); } static Operator POSITIVE = new Operator() { public int getLevel() { return 3; } public Literal calculate(Literal... literals) { return literals[0]; } }; static Operator NEGATIVE = new Operator() { public int getLevel() { return 3; } public Literal calculate(Literal... literals) { return new Literal(-literals[0].value); } }; static Operator MUL = new Operator() { public int getLevel() { return 2; } public Literal calculate(Literal... literals) { return new Literal(literals[0].value * literals[1].value); } }; static Operator DIV = new Operator() { public int getLevel() { return 2; } public Literal calculate(Literal... literals) { return new Literal(literals[0].value / literals[1].value); } }; static Operator ADD = new Operator() { public int getLevel() { return 1; } public Literal calculate(Literal... literals) { return new Literal(literals[0].value + literals[1].value); } }; static Operator SUB = new Operator() { public int getLevel() { return 1; } public Literal calculate(Literal... literals) { return new Literal(literals[0].value - literals[1].value); } }; static Operator LEFT_WRAP = new Operator() { public int getLevel() { return -1; } public Literal calculate(Literal... literals) { return null; } }; static Operator RIGHT_WRAP = new Operator() { public int getLevel() { return -1; } public Literal calculate(Literal... literals) { return null; } }; enum State { IDLE, AFTER_LITERAL, AFTER_OPERATOR } static class Lexer { final int len; final String text; private int pos; State state; private Stack<IToken> stack; private Stack<Operator> opStack; Lexer(String input) { text = input; pos = 0; len = text.length(); state = State.IDLE; stack = new Stack<IToken>(); opStack = new Stack<Operator>(); IToken token = null; while ((token = next()) != null) { if (token instanceof Literal) { if (state == State.IDLE || state == State.AFTER_OPERATOR) { stack.push(token); state = State.AFTER_LITERAL; continue; } else { throw new IllegalArgumentException("Unexpected token Literal"); } } else { Operator op = (Operator) token; if (op == LEFT_WRAP) { opStack.push(LEFT_WRAP); state = State.IDLE; } else if (op == RIGHT_WRAP) { popUtilLeftWrap(); state = State.AFTER_LITERAL; } else { if (op == ADD) { if (state == State.IDLE || state == State.AFTER_OPERATOR) { op = POSITIVE; } state = State.AFTER_OPERATOR; } else if (op == SUB) { if (state == State.IDLE || state == State.AFTER_OPERATOR) { op = NEGATIVE; } state = State.AFTER_OPERATOR; } else { if (state == State.AFTER_LITERAL) { state = State.AFTER_OPERATOR; } else { throw new IllegalArgumentException("Unexpected token MUL/DIV"); } } popUtilLevel(op.getLevel()); opStack.push(op); } } } popUtilLevel(-2); } private void popUtilLevel(int level) { while (!opStack.empty() && opStack.peek().getLevel() >= level) { stack.push(opStack.pop()); } } private void popUtilLeftWrap(){ while (!opStack.empty() && opStack.peek()!= LEFT_WRAP) { stack.push(opStack.pop()); } if(opStack.peek() == LEFT_WRAP){ opStack.pop(); }else{ throw new IllegalArgumentException("Unmatched RIGHT_WRAP"); } } private IToken next() { for (; pos < len; pos++) { if (!Character.isWhitespace(text.charAt(pos))) { break; } } if (pos == len) { return null; } char ch = text.charAt(pos); if (Character.isDigit(ch)) { int start = pos; for (; pos < len; pos++) { if (!Character.isDigit(text.charAt(pos))) { break; } } return new Literal(Integer.valueOf(text.substring(start, pos))); } pos++; switch (ch) { case '+': { return ADD; } case '-': { return SUB; } case '*': { return MUL; } case '/': { return DIV; } case '(': { return LEFT_WRAP; } case ')': { return RIGHT_WRAP; } default: { throw new IllegalArgumentException("Unacceptable char: " + ch); } } } int calculate() { int size = stack.size(); IToken[] tokens = stack.toArray(new IToken[size]); Stack<Literal> result = new Stack<Literal>(); for(int i = 0; i < size; i++){ IToken token = tokens[i]; if(token instanceof Literal){ result.push((Literal)token); }else{ Operator op = (Operator)token; if(op == POSITIVE || op == NEGATIVE){ if(!result.empty()){ Literal literal = result.pop(); result.push(op.calculate(literal)); }else{ throw new IllegalArgumentException("Unexpected OP1"); } }else{ if(result.size() >= 2){ Literal right = result.pop(); Literal left = result.pop(); result.push(op.calculate(left,right)); }else { throw new IllegalArgumentException("Unexpected OP2"); } } } } if(result.size() == 1){ return result.pop().value; }else{ throw new IllegalArgumentException("Unexpected result"); } } } public static void main(String[] args){ String[] examples = new String[]{ "(1+2)*-3", "((-2+3)*(2+4-3/2)+5*6)-7", "-1*9+5*6/3" }; for(String example :examples){ try{ System.out.println("" + example + "=" + (new Lexer(example).calculate())); }catch (Exception e){ e.printStackTrace(); } } } }