Potentially dumb: Assuming I have a string containing an operator what's the best way to apply this operator ?
What i tend to do is :
if(n.getString(1).equals("<<")) {
result = tmp1 << tmp2;
}
for each kind of operator I have. Is there a better way ?
Not sure whether you'd call this elegant, but here is one way:
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
}};
Then you could replace your if
statement with:
result = operations.get(n.getString(1)).apply(tmp1, tmp2);
Could do something like:
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
}
the return the right one will be really nice once switch statements are go for String
s.
This solution looks like the following in use (sans Operator.
if you use a static import):
int result = Operator.which(s).apply(a,b);
but I'd go with someone else's widely-tested and used parser.
The object oriented way to do it would be to use an enumeration of the possible operations. That way each operation could only consume one object in memory.
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);
}
To call such code, you would then do something like:
int result = Operation.ADD(5, 6);
Then you could create a map of Strings to Operations, like so:
Map<String, Operation> symbols = new Map<String, Operation>();
symbols.put("+", Operation.ADD);
symbols.put("-", Operation.SUBTRACT);
symbols.put("/", Operation.DIVIDE);
symbols.put("*", Operation.MULTIPLY);
...
Finally, to use such a system:
symbols.get(n.getString(1).apply(tmp1, tmp2));
One advantage to using enumerations in this manner is that you have the luxury of comparing the operations on the data, should you choose to do so
Operation operation = symbols.get("*");
if (operation != Operation.MULTIPLY) {
System.out.println("Foobar as usual, * is not multiply!");
}
In addition, you get a centralized location for all operations, the only downside to this is that the Operation.java file might grow large with a sufficiently large set of operators.
The only issues that might exist long-term is that while such a system is useful and easy to read and understand, it really doesn't take into account precedence. Assuming your formula are all evaluated in the order of precedence, such a problem doesn't matter. Examples of expressing the formula in the order of precedence can be found in Reverse Polish Notation, Polish Notation, etc.
Where precedence does matter is when you are allowed to express items like:
4 + 5 * 2
where according to typical convention, the 5 * 2 should be evaluated before the 4 + 5. The only correct way to handle precedence is to form an evaluation tree in memory, or to guarantee that all input handles precedence in a simple, unambiguous manner (Polish Notation, Reverse Polish Notation, etc).
I'm assuming you know about the precedence issues, but thank you for letting me mention it for the benefit of those who haven't had to write such code yet.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With