I have a list of num and string of operator above:
final List<dynamic> items = [5.0, "-", 2, "*", 3];
I want to calculate the value inside of it. I can do like this:
final List<dynamic> items = [5.0, "-", 2, "*", 3]; //correct is -1
num result = 0;
String op = " ";
for (final item in items) {
if (item is String) {
op = item;
}
if (item is num) {
switch (op) {
case " ":
result = result item;
break;
case "-":
result = result - item;
break;
case "*":
result = result * item;
break;
case "/":
result = result / item;
break;
}
}
}
print(result); // incorrect result: 9
As above code, If it is just an " " or "-" operator it would return the correct answer, but because of the order of operators "/" and "*" doing like above return incorrect result.
Can anyone suggest any algorithm?
CodePudding user response:
Here is one way of doing it, the code works but I didn't put the tests that would be necessary to check for bad input.
// functions for basic operations
num multiply(num leftOp, num rightOp) => leftOp * rightOp;
num divide(num leftOp, num rightOp) => leftOp / rightOp;
num add(num leftOp, num rightOp) => leftOp rightOp;
num substract(num leftOp, num rightOp) => leftOp - rightOp;
void main() {
final List<dynamic> items = [5.0, "-", 2, "*", 3]; //correct is -1
num result = 0;
// Copy the list in a temporary list
var calc = [...items];
// set the precedence order of the operators
// create 2 groups of equal importance
var operators = [
{
"*": multiply,
"/": divide,
},
{
" ": add,
"-": substract,
}
];
//calc.reduce((value, element) => value element);
// loop until all operators have produced result
while (calc.length > 1) {
for (var opPrecedence in operators) {
// find first operator in a group, starting from left
var pos = calc.indexWhere((e) => opPrecedence.containsKey(e));
if (pos >= 0) {
num leftOp = calc[pos - 1];
num rightOp = calc[pos 1];
var operation = opPrecedence[calc[pos]];
result = operation!(leftOp, rightOp);
// remove the 2 operands and replace with result
calc.removeAt(pos);
calc.removeAt(pos);
calc[pos - 1] = result;
}
}
}
// what should be left is the final result
print(calc[0]);
}
CodePudding user response:
By combined ManuH68 answer I did:
void main() {
test('Return correct', () {
const correctResult = 10;
final calc = <dynamic>[2, "*", 5, "/", 2, "-", 3, " ", 8];
num result = 0;
// clean "*" and "/" first
while (calc.contains("*") || calc.contains("/")) {
final pos = calc.indexWhere((e) => e == "*" || e == "/");
num leftOp = calc[pos - 1];
num rightOp = calc[pos 1];
switch (calc[pos]) {
case "*":
result = leftOp * rightOp;
break;
case "/":
result = leftOp / rightOp;
break;
}
calc.removeAt(pos);
calc.removeAt(pos);
calc[pos - 1] = result;
}
// Then After calculated "*" and "/" perform calculate on " " and "-"
while (calc.length > 1) {
final pos = calc.indexWhere((e) => e == "-" || e == " ");
num leftOp = calc[pos - 1];
num rightOp = calc[pos 1];
switch (calc[pos]) {
case "-":
result = leftOp - rightOp;
break;
case " ":
result = leftOp rightOp;
break;
}
calc.removeAt(pos);
calc.removeAt(pos);
calc[pos - 1] = result;
}
expect(calc[0], correctResult);
});
}
this might not clean but this is what I founded that work