【问题标题】:Re-Order array based on priority in Java基于Java中的优先级重新排序数组
【发布时间】:2014-01-04 13:45:26
【问题描述】:

您好,我一直在研究我的 Java 计算器。它现在将所有操作数和运算符放在不同的数组中,并且我还有一个数组,每个运算符的优先级。

For example * and / are 1 and + and - are 2.
So if my operator array holds '[/, +, +, *]'
My priority array holds '[1, 2, 2, 1]'

我一直坚持的部分是我现在需要确保我的计算器可以重新排序运算符,以便我可以通过我的calculateResult 工作并确保运算符的顺序正确。

我寻求帮助的部分是计算结果。我不知道如何去做,以便它以正确的顺序计算它。

import java.util.*;
public class stringCalculator {

    //String containing the input from the user
    private String userinput;
    //List to store the operators in
    private ArrayList<String> calcOperator = new ArrayList<String>();
    //List to store the operands in
    private ArrayList<Integer> calcOperand = new ArrayList<Integer>();
    //Array to store all the integers in
    private String[] integers;
    //Array to store all the operators in
    private String[] operators;
    //Array to store the priority value
    private String[] priorityList;

    public stringCalculator(String userinput){
        this.userinput = userinput;
        //System.out.println(userinput);
        integers = userinput.split("[+-/*///]");
        operators = userinput.split("\\d+");
    }   

    //This function will check the input and return true if the user enters a correct expression.
    public boolean checkInput(){
        boolean show = userinput.matches("[-+/*0-9]+");
        return show;
    }

    //This function will add any numbers in the string to the calcOperand array.
    //and any operators to the calcOperator field.
    public void parseInput(String[] item){
        for (String op : item){
            if (op.matches("\\d+")){
                calcOperand.add(Integer.parseInt(op));
            }
            //operators go into calcOperators.
            else if (op.equals("+")||op.equals("-")||op.equals("*")||op.equals("/")){
                calcOperator.add(op);
            }
            else{//everything else is ignored and left. 
            }
        }
    }

    //Function to calculate the priority of each operator.
    //For example * and / will be 1, and + and - will be 2.
    public void calculatePriority(){
        priorityList = calcOperator.toArray(new String[calcOperator.size()]);
        for (int i = 0; i<priorityList.length; i++){
            if (priorityList[i].equals("+")){
                priorityList[i] = "2";
            }else if (priorityList[i].equals("-")) {
                    priorityList[i] = "2";
            }else if (priorityList[i].equals("/")){
                priorityList[i] = "1";
            }else if (priorityList[i].equals("*")){
                priorityList[i] = "1";
            }else{
                System.out.println("error");
            }
        }
    }
    public void printPri(){

        for (String s : priorityList)
            System.out.print(s +",");
    }

    //Function to show the result of the expression.
    public void calculateResult(){
        if(checkInput()){
            parseInput(integers);
            parseInput(operators);
            System.out.println("Operands: " + calcOperand);
            System.out.println("Operators: " + calcOperator);
            calculatePriority();
            System.out.print("Priority: ");
            printPri();
        }else{
            System.out.println("Please enter a valid input!");

        }
    }

}

【问题讨论】:

    标签: java arrays string calculator


    【解决方案1】:

    生成操作树的一种方法的非常基本的示例。

    我已经将这些类嵌套在一个 Test 类中(因为我不想处理额外的文件),但它应该足够简单,可以解绑。

    • MathNode 是一个抽象类,代表树中的一个节点;
    • ElementNode 是 MathNode 的子类,代表一个数值(和树的叶子);
    • SumNode 是 MathNode 的子类,代表一个操作;
    • tokenize() 遍历字符串查找操作(或字符串结尾)和前面的数字,并创建适当的标记并将它们附加到数组中。
    • makeTree() 遍历标记数组,当找到当前正在搜索的操作时,将树的左右节点设置为紧接在前面和后面的标记(一旦添加到树中,就从数组中删除这些标记)。
    • 运行tokenize() 然后makeTree() 四次,每个/、*、- 和+ 操作,将构建树,其中单个节点留在数组中令牌是树的根。

    可以包含更多错误检查,我相信可以显着提高树的构建效率(并且不需要四次通过),但希望这能让您了解如何继续。

    代码:

    import java.util.ArrayList;
    
    public class Test
    {
        public static enum Operation { MULTIPLY, DIVIDE, ADD, SUBTRACT };
        abstract static class MathNode {
            public abstract double calc();
            public abstract String toString();
            public abstract boolean set( final MathNode left, final MathNode right, final Operation op );
        }
        static class ElementNode extends MathNode {
            private final double value;
            public ElementNode( final double v ) {
                this.value = v;
            }
            public double calc() {
                return value;
            }
            public String toString() {
                return Double.toString( value );
            }
            public boolean set( final MathNode left, final MathNode right, final Operation op ){
                return false;
            }
        }
        static class SumNode extends MathNode {
            public MathNode left = null;
            public MathNode right = null;
            public final Operation op;
            public SumNode( final Operation op ){
                this.op = op;
            }
            public boolean set( final MathNode left, final MathNode right, final Operation op ){
                if ( this.op == op )
                {
                    this.left = left;
                    this.right = right;
                    return true;
                }
                return false;
            }
            public double calc() {
                final double l = left  == null ? 0 : left.calc();
                final double r = right == null ? 0 : right.calc();
                switch ( this.op ){
                case MULTIPLY:  return l * r;
                case DIVIDE:    return l / r;
                case SUBTRACT:  return l - r;
                default:        return l + r;
                }
            }
            public String toString(){
                final String l = left  == null?"0":left.toString();
                final String r = right == null?"0":right.toString();
                switch ( this.op ){
                case MULTIPLY:  return "( " + l + " * " + r + " )";
                case DIVIDE:    return "( " + l + " / " + r + " )";
                case SUBTRACT:  return "( " + l + " - " + r + " )";
                default:        return "( " + l + " + " + r + " )";
                }
            }
        }
        public static ArrayList<MathNode> tokenize( final String sum )
        {
            int i = 0,
                p = 0;
            final int l = sum.length();
            final ArrayList<MathNode> tokens = new ArrayList<MathNode>();
            while ( i < l )
            {
                final SumNode sn;
                switch ( sum.charAt(i) ){
                case '*':   sn = new SumNode( Operation.MULTIPLY ); break;
                case '/':   sn = new SumNode( Operation.DIVIDE );       break;
                case '+':   sn = new SumNode( Operation.ADD );      break;
                case '-':   sn = new SumNode( Operation.SUBTRACT ); break;
                default:
                    // TODO: Add something to check if number is valid
                    ++i; 
                    continue;
                }
                // TODO: Add something to check for zero-width numbers 
                final double value = Double.parseDouble( sum.substring( p, i ) );
                p = ++i;
                tokens.add( new ElementNode( value ) );
                tokens.add( sn );
            }
            // TODO: Add something to check for zero-width numbers
            final double value = Double.parseDouble( sum.substring( p ) );
            tokens.add( new ElementNode( value ) );
            return tokens;
        }
        public static void makeTree( final ArrayList<MathNode> tokens, final Operation op ){
            for ( int i = tokens.size() - 2; i >= 1; --i )
            {
                final MathNode node = tokens.get( i );
                if ( node.set( tokens.get(i-1), tokens.get(i+1), op) )
                {
                    tokens.remove( i + 1 );
                    tokens.remove( i - 1 );
                    --i;
                }
            }
        }
        public static void main(final String[] args) {
            final String sum = "23.2-5.2*4.4/2.2+14";
            final ArrayList<MathNode> tokens = tokenize( sum );
            makeTree( tokens, Operation.DIVIDE );
            makeTree( tokens, Operation.MULTIPLY );
            makeTree( tokens, Operation.SUBTRACT );
            makeTree( tokens, Operation.ADD );
            final MathNode sum_tree = tokens.get(0);
            System.out.println( sum_tree + " = " + sum_tree.calc() );
        }
    }
    

    【讨论】:

    • 您好 MTO,感谢您的回答。我一直在玩代码,想知道 'double l = left == null 行是什么? 0 : left.calc();'和'double r = right == null ? 0 : right.calc();'除了为 l 和 r 赋值之外,实际上还做了什么?
    • 该行等价于:double l; if ( left == null ) { l = 0; } else { l = left.calc(); }(右边也一样)。 SumNodes(代表和中的操作)形成一棵二叉树(ElementNodes 在叶子上,代表和中的数字)所有这些行所做的就是查看在tree - 如果是,则计算该子树的总和结果,如果不是,则返回零。它只是一个计算,不会做任何修改二叉树结构或改变任何事情的事情。
    • 好的,我明白了。我遇到的唯一另一个问题是了解 makeTree 方法的工作原理。为什么在 for 循环和 if 语句中都需要 --i 。另外,您将如何更改代码以查看是否已在字符串中使用任何括号以确保它们优先于表达式的其余部分。非常感谢,Ciaran。
    • 括号将很难使用该方法(因为它非常基本),您可能需要重写标记器函数以将总和从中缀转换为后缀排序,然后从中生成语法树.我已经通过重写获得了 99%,但我只是想找到最后几个错误,完成后我会发布一个编辑。
    • 这两条--i语句是因为:for语句中的一条用于遍历节点; if 语句中的一个用于当前节点包含正在处理的运算符,然后从列表中删除紧邻的前面和后面的节点(成为二叉树中该当前节点的子节点) - 在在这种情况下,您不想处理前一个节点,因为它已被删除,因此您需要再减少一次计数器。
    【解决方案2】:

    执行此操作的正确方法是将输入解析为 AST(抽象语法树)。然后以正确的顺序对表达式进行求值。

    试图将所有问题简化为表格解决方案的倾向是一种巨大的反模式,遗憾的是,许多程序员从未学习过这种模式。并非所有东西都是一张桌子。那里也有树木和图表。

    几乎所有的解析作业都应该构建一棵树(这可能是也可能不是复合模式的实现)。

    【讨论】:

    • 我真正想做的是评估输入字符串中描述的表达式的值并返回结果。我能不能把这些数组放在一个堆栈上,然后作为一个反向波兰符号计算器工作?
    【解决方案3】:

    您真的需要扫描所有运算符、计算优先级并重新排序所有内容吗?

    如果您只是检查所有运算符和值,并且只评估 * 和 / 会怎样

    然后再次检查并评估+ 和-

    可能比您尝试做的要容易得多。

    【讨论】:

      猜你喜欢
      • 2016-03-14
      • 1970-01-01
      • 1970-01-01
      • 2021-01-24
      • 2014-08-05
      • 1970-01-01
      • 2018-11-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多