代码之家  ›  专栏  ›  技术社区  ›  LB40

应用java中作为字符串的运算符的最优雅的方法是什么?

  •  5
  • LB40  · 技术社区  · 14 年前

    潜在哑巴: 假设我有一个包含运算符的字符串,应用此运算符的最佳方法是什么?

    if(n.getString(1).equals("<<")) {
      result = tmp1 << tmp2;
    }
    

    对于我拥有的每种操作员。有更好的办法吗?

    10 回复  |  直到 14 年前
        1
  •  7
  •   Carl    14 年前

    enum Operator {
    
     BITSHIFT { ... }, ADD { ... }, XOR { ... }, //...etc
    
     Operator public static which(String s) { //...return the correct one
     }
    
     public abstract int apply(int a, int b); //...defined explicitly for each enum
    
    }
    

    一旦开始使用switch语句,正确的返回将非常好 String

    此解决方案在使用中类似于以下内容(sans Operator. 如果使用静态导入):

     int result = Operator.which(s).apply(a,b);
    

    但我会选择其他人广泛测试和使用的解析器。

        2
  •  15
  •   missingfaktor Kevin Wright    14 年前

    我不确定你是否认为这很优雅,但有一种方法:

    interface Operation {
      int apply(int a, int b);
    }
    
    Map<String, Operation> operations = new HashMap<String, Operation>() {{
      put("+", new Operation() { public int apply(int a, int b) { return a + b; }});
      put("-", new Operation() { public int apply(int a, int b) { return a - b; }});
      put("*", new Operation() { public int apply(int a, int b) { return a * b; }});
      put("<<", new Operation() { public int apply(int a, int b) { return a << b; }});
      // some more operations here
    }};
    

    然后你可以替换你的 if

    result = operations.get(n.getString(1)).apply(tmp1, tmp2);
    
        3
  •  5
  •   Edwin Buck    14 年前

    public enum Operation {
    
    
      ADD() {
        public int perform(int a, int b) {
          return a + b;
        }
      },
      SUBTRACT() {
        public int perform(int a, int b) {
          return a - b;
        }
      },
      MULTIPLY() {
        public int perform(int a, int b) {
          return a * b;
        }
      },
      DIVIDE() {
        public int perform(int a, int b) {
          return a / b;
        }
      };
    
      public abstract int perform(int a, int b);
    
    }
    

    要调用此类代码,您可以执行以下操作:

    int result = Operation.ADD(5, 6);
    

    然后可以创建字符串到操作的映射,如下所示:

    Map<String, Operation> symbols = new Map<String, Operation>();
    symbols.put("+", Operation.ADD);
    symbols.put("-", Operation.SUBTRACT);
    symbols.put("/", Operation.DIVIDE);
    symbols.put("*", Operation.MULTIPLY);
    ...
    

    symbols.get(n.getString(1).apply(tmp1, tmp2));
    

    Operation operation = symbols.get("*");
    if (operation != Operation.MULTIPLY) {
      System.out.println("Foobar as usual, * is not multiply!");
    }
    

    此外,所有操作都有一个集中的位置,唯一的缺点是Operation.java文件可能会随着一组足够大的操作符而变大。

    唯一可能长期存在的问题是,虽然这样一个系统很有用,也很容易阅读和理解,但它确实没有考虑到优先级。假设你的公式都是按优先顺序计算的,这样的问题无关紧要。按照优先顺序表示公式的例子可以在反向波兰符号、波兰符号等中找到。

    4 + 5 * 2
    

    根据典型的惯例,5*2应该在4+5之前计算。处理优先级的唯一正确方法是在内存中形成一个计算树,或者保证所有输入都以简单、明确的方式处理优先级(波兰表示法、反向波兰表示法等)。

    我假设您知道优先级问题,但是感谢您让我提到它,这是为了那些还没有编写此类代码的人。

        4
  •  3
  •   Hank Gay    14 年前

    JavaCC ANTLR 可能是Java世界中最流行的两个解析器生成器。

    如果解析器生成器过于复杂(可能是因为您没有/不想创建正式语法),那么您可以使用 Ragel 实现自定义解析器。

        5
  •  2
  •   alex    14 年前

    除非您有一个大的运算符集,或者希望将来能够允许使用更复杂的表达式,否则就不必了。

    final Map<String,Function<(X,X), Y>> OPERATORS = { 
        "<<" : (x,y)->{x << y}, 
        "+" : (x,y)->{x + y},
        [...]
    };
    
    [...]
    
    result1 = OPERATORS.get(n.getString(1))(tmp1, tmp2);
    

    您可以用Java编写,但是由于缺少简洁的映射文本和匿名类声明,所以要详细得多。

        6
  •  2
  •   Robin    14 年前

    对于这种类型的场景,枚举是干净的。

    public enum Operator
    {
        ADD("+")
        {
            public int apply(int a, int b)
            {
                return a + b;
            }
        }
    
        SHIFT_LEFT("<<")
        {
            public int apply(int a, int b)
            {
                return a << b;
            }
        }
    
        private String opString;
    
        private Operator(String op)
        {
            opString = op
        }
    
        static public Operator getOperator(String opRep)
        {
            for (Operator o:values())
            {
                if (o.opString.equals(opRep))
                    return o;
            }
            throw new IllegalArgumentException("Operation [" + opRep + "] is not valid");
        }
    
        abstract public int apply(int a, int b);
    }
    

    result = Operator.getOperator("<<").apply(456,4);
    

    另一个选择是包含一个静态方法,该方法接受运算符和操作数的字符串表示形式,并具有单个方法调用,尽管我更喜欢将它们分开。

        7
  •  1
  •   Chris Dennett    14 年前

    您可以使用JSR-223解释性语言,比如BeanShell,让它执行操作,然后返回结果。

        8
  •  1
  •   OscarRyz    14 年前

    也可以使用操作符的hashCode并将其放在 switch

    如果您不经常添加运算符(我认为您不会这样做),这可能会起作用

    所以这就足够了:

    String op = "+";
    
    switch( op.hashCode() ){
        case ADD: r = a +  b;break;
        case SUB: r = a -  b;break;
        case TMS: r = a *  b;break;
        case DIV: r = a /  b;break;
        case MOD: r = a %  b;break;
        case SLF: r = a << b;break;
        case SRG: r = a >> b;break;
        case AND: r = a &  b;break;
        case OR:  r = a |  b;break;
        case XOR:  r = a ^ b;break;
        default: out.print("Eerr!!!"); break;
    }
    

    并拥有 AND 定义为:

    private static final int ADD = 0x2b;
    

    下面是完整的运行示例代码:

    import static java.lang.System.out;
    class Evaluator {
    
        private static final int ADD = 0x2b;    // +
        private static final int SUB = 0x2d;    // -
        private static final int TMS = 0x2a;    // *
        private static final int DIV = 0x2f;    // /
        private static final int MOD = 0x25;    // %
        private static final int SLF = 0x780;    // <<
        private static final int SRG = 0x7c0;    // >>
        private static final int AND = 0x26;    // &
        private static final int OR  = 0x7c;    // |
        private static final int XOR  = 0x5e;    // ^
    
        private int r;
        private int a;
        private String op;
        private int b;
    
        private Evaluator( int a, String op, int b ) {
            this.a = a; this.op = op; this.b = b;
        }
        private Evaluator eval() {
            switch( op.hashCode() ){
                case ADD: r = a +  b;break;
                case SUB: r = a -  b;break;
                case TMS: r = a *  b;break;
                case DIV: r = a /  b;break;
                case MOD: r = a %  b;break;
                case SLF: r = a << b;break;
                case SRG: r = a >> b;break;
                case AND: r = a &  b;break;
                case OR:  r = a |  b;break;
                case XOR:  r = a ^ b;break;
                default: out.print("Eerr!!!"); break;
            }
            return this;
        }
    
        // For testing:
        public static int evaluate( int a, String op ,  int b ) {
            return new Evaluator(a, op, b).eval().r;
        }
    
        public static void main( String [] args ) {
            out.printf( " 1 + 2   = %d%n", evaluate( 1 ,"+" , 2 ));
            out.printf( " 1 - 2   = %d%n", evaluate( 1 ,"-" , 2 ));
            out.printf( " 1 * 2   = %d%n", evaluate( 1 ,"*" , 2 ));
            out.printf( " 1 / 2   = %d%n", evaluate( 1 ,"/" , 2 ));
            out.printf( " 1 %% 2  = %d%n", evaluate( 1 ,"%" , 2 ));
            out.printf( " 1 << 2  = %d%n", evaluate( 1 ,"<<" , 2 ));
            out.printf( " 1 >> 2  = %d%n", evaluate( 1 ,">>" , 2 ));
            out.printf( " 1 & 2   = %d%n", evaluate( 1 ,"&" , 2 ));
            out.printf( " 1 |  2  = %d%n", evaluate( 1 ,"|" , 2 ));
            out.printf( " 1 ^ 2   = %d%n", evaluate( 1 ,"^" , 2 ));
        }
    }
    

    就这样。运行速度非常快(我很确定枚举是有序的)

    从面向对象的角度来看,我认为Rahul的答案是可以的,但是如果你想认真对待这个问题,你应该像Hank Gay建议的那样使用解析器。

    附笔 获取字符串的哈希代码很容易:

    System.out.println( "+".hashCode() );
    

    实际上我用了这个:

    public class HexHashCode {
        public static void main( String [] args ) {
            for( String s: args ) {
                System.out.printf("private final int %s = 0x%x;    // %s\n",s,s.hashCode(), s );
            }
        }
    }
    

    java HexHashCode + - "*" / "<<" ">>" "%" "<" ">" "==" "!=" "&" "|"  "^" 
    
        9
  •  1
  •   ahmed ansari    14 年前

    用c语言很容易做到。

    var ops = new Dictionary<string, Func<int, int, int>> {
      {"+", (a, b) => a + b},
      {"-", (a, b) => a - b},
      {"*", (a, b) => a * b},
      {"/", (a, b) => a / b}
    };
    

    测试:

    var c = ops["+"](2, 3);
    Console.WriteLine(c);
    

        10
  •  0
  •   user268396    14 年前

    @Robin:在Java中,非抽象类中不能有抽象方法,枚举类型也不能是抽象的(因为所有的枚举类型都继承自枚举,这意味着一个枚举类型不能通过Java的继承规则继承自另一个枚举类型)

    另外,您可以添加一个名为register的静态方法:

    private static final Map<String, Operator> registered=new HashMap<String,Operator>();
    private static void register(String op, Operator impl) {
       registered.put(op, impl);
    }
    

    然后在构造函数中调用该方法;并在getOperator()方法中使用map lookup。