首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用后缀转换和评估的Java计算器

使用后缀转换和评估的Java计算器
EN

Code Review用户
提问于 2015-08-17 08:27:02
回答 1查看 15.3K关注 0票数 5

我正在尝试创建一个程序,将infix表达式转换为后缀(使用堆栈),并计算后缀表达式的结果。我已经有了一个可行的解决方案,但我觉得它很难看,而且必须有一个更好的方法。

(注意:程序必须能够接受大于9的数字和十进制数,所以它比通常的转换器要复杂一些)

Converter类:

代码语言:javascript
复制
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;


public class PostFixConverter {
    private String infix; // The infix expression to be converted
    private Deque<Character> stack = new ArrayDeque<Character>();
    private List<String> postfix = new ArrayList<String>(); // To hold the postfix expression

    public PostFixConverter(String expression)
    {
        infix = expression;
        convertExpression();
    }

    /* The approach is basically, if it's a number, push it to postfix list
     * else if it's an operator, push it to stack
     */
    private void convertExpression()
    {
        // Temporary string to hold the number
        StringBuilder temp = new StringBuilder();

        for(int i = 0; i != infix.length(); ++i)
        {           
            if(Character.isDigit(infix.charAt(i)))
            {
                /* If we encounter a digit, read all digit next to it and append to temp
                 * until we encounter an operator.
                 */
                temp.append(infix.charAt(i));

                while((i+1) != infix.length() && (Character.isDigit(infix.charAt(i+1)) 
                                                  || infix.charAt(i+1) == '.'))
                {
                    temp.append(infix.charAt(++i));
                }


                /* If the loop ends it means the next token is an operator or end of expression
                 * so we put temp into the postfix list and clear temp for next use
                 */
                postfix.add(temp.toString());
                temp.delete(0, temp.length());
            }
            // Getting here means the token is an operator
            else
                inputToStack(infix.charAt(i));
        }
        clearStack();
    }


    private void inputToStack(char input)
    {
        if(stack.isEmpty() || input == '(')
            stack.addLast(input);
        else 
        {
            if(input == ')')
            {
                while(!stack.getLast().equals('('))
                {
                    postfix.add(stack.removeLast().toString());
                }
                stack.removeLast();
            }
            else 
            {
                if(stack.getLast().equals('('))
                    stack.addLast(input);
                else
                {
                    while(!stack.isEmpty() && !stack.getLast().equals('(') && 
                            getPrecedence(input) <= getPrecedence(stack.getLast()))
                    {
                        postfix.add(stack.removeLast().toString());
                    }
                    stack.addLast(input);
                }
            }
        }
    }


    private int getPrecedence(char op)
    {
         if (op == '+' || op == '-')
                return 1;
         else if (op == '*' || op == '/')
                return 2;
         else if (op == '^')
                return 3;
         else return 0;
    }


    private void clearStack()
    {
        while(!stack.isEmpty())
        {
            postfix.add(stack.removeLast().toString());
        }
    }


    public void printExpression()
    {
        for(String str : postfix)
        {
            System.out.print(str + ' ');
        }
    }


    public List<String> getPostfixAsList()
    {
        return postfix;
    }
}

计算器类:

代码语言:javascript
复制
import java.math.BigDecimal;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;


public class PostFixCalculator {
    private List<String> expression = new ArrayList<String>();
    private Deque<Double> stack = new ArrayDeque<Double>();

    public PostFixCalculator(List<String> postfix) {expression = postfix;}


    public BigDecimal result()
    {
        for(int i = 0; i != expression.size(); ++i)
        {
            // Determine if current element is digit or not
            if(Character.isDigit(expression.get(i).charAt(0)))
            {
                stack.addLast(Double.parseDouble(expression.get(i)));
            }
            else 
            {
                double tempResult = 0;
                double temp;

                switch(expression.get(i))
                {
                    case "+": temp = stack.removeLast();
                              tempResult = stack.removeLast() + temp;
                              break;

                    case "-": temp = stack.removeLast();
                              tempResult = stack.removeLast() - temp;
                              break;

                    case "*": temp = stack.removeLast();
                              tempResult = stack.removeLast() * temp;
                              break;

                    case "/": temp = stack.removeLast();
                              tempResult = stack.removeLast() / temp;
                              break;
                }
                stack.addLast(tempResult);
            }
        }
        return new BigDecimal(stack.removeLast()).setScale(3, BigDecimal.ROUND_HALF_UP);
    }
}

下面是程序的一些测试运行:

代码语言:javascript
复制
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;


public class ConsoleCalc {

    public static void main(String[] args) throws IOException
    {
        String expression;
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        System.out.print("Enter expression: ");
        expression = br.readLine();

        PostFixConverter pc = new PostFixConverter(expression); 
        System.out.print("Postfix: "); 
        pc.printExpression();

        PostFixCalculator calc = new PostFixCalculator(pc.getPostfixAsList());
        System.out.println();
        System.out.println("Result: " + calc.result());
    }
}

结果:

输入表达式: 10.2*(8-6)/3+112.5后缀: 10.2 8 6-*3/ 112.5 +结果: 119.300

我特别关注转换器类中的convertExpression方法。我用临时StringBuilder逐个数字读取数字标记的方式真的很难看。

EN

回答 1

Code Review用户

发布于 2015-08-17 10:38:52

在语法分析器中常见的是将词法标记部分从语义语法分析中分离出来。您可以在unix解析器工具中看到这一点,这些工具有词法工具flex/lex和解析器工具bison/yacc。

我会考虑在您的代码中进行更大的分离。创建一个新的Token类来保存单个令牌,并创建一个新的Tokenizer类,其任务是将输入拆分为一个令牌列表。然后,解析器将处理令牌列表并生成堆栈。这样就可以更容易地识别bug,即词法部分或语法解析中的bug。它还将使代码的扩展更加容易。

在我的代码中,对于数字、运算符、变量名等,我有不同的Token子类。我使用正则表达式来匹配不同类型的令牌。我使用(\d+\.?\d*)|(\.\d+)来匹配数字。\d匹配单个数字,\d+匹配一个或多个十进制数字,\d*匹配零或多个数字,\.匹配文字小数点,\.?匹配可选小数点,()将子模式组合在一起,|匹配lhs或rhs。因此,lhs将匹配一组没有小数点位的数字,或一组后面跟着小数位和可选数字的数字。rhs将匹配小数位,后面是一组数字。这意味着整个regexp将匹配123,123,123.45,.45。

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/101160

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档