Please note, this blog entry is from a previous course. You might want to check out the current one.
Make your description have as few parentheses as possible for binary operations.
Add a new computed variable to the Op enum, which returns by the default the maximum integer value and for binary operations the value you set when defining the operation:
private enum Op: Printable { ... case BinaryOperation(String, Int, (Double, Double) -> Double) ... var precedence: Int { get { switch self { case .BinaryOperation(_, let precedence, _): return precedence default: return Int.max } } } }
For multiplications and divisions set the value to “2” and for additions and subtractions to “1”:
init() { ... learnOp(Op.BinaryOperation("ร", 2, *)) learnOp(Op.BinaryOperation("รท", 2, { $1 / $0 })) learnOp(Op.BinaryOperation("+", 1, +)) learnOp(Op.BinaryOperation("โ", 1, { $1 - $0 })) ... }
The API of the recursive description method gets an additional return variable holding the precedence of the current operation. For binary operations check if the current precedence is higher than the one from the previous operation. If yes, add brackets. Do the same for unary operations – even it is not required, it looks nicer ๐
private func description(ops: [Op]) -> (result: String?, remainingOps: [Op], precedence: Int?) { ... case .Operand(let operand): return (String(format: "%g", operand) , remainingOps, op.precedence) case .NullaryOperation(let symbol, _): return (symbol, remainingOps, op.precedence); case .UnaryOperation(let symbol, _): let operandEvaluation = description(remainingOps) if var operand = operandEvaluation.result { if op.precedence > operandEvaluation.precedence { operand = "(\(operand))" } return ("\(symbol)\(operand)", operandEvaluation.remainingOps, op.precedence) } case .BinaryOperation(let symbol, _, _): let op1Evaluation = description(remainingOps) if var operand1 = op1Evaluation.result { if op.precedence > op1Evaluation.precedence { operand1 = "(\(operand1))" } let op2Evaluation = description(op1Evaluation.remainingOps) if let operand2 = op2Evaluation.result { if op.precedence > op2Evaluation.precedence { operand2 = "(\(operand2))" } return ("\(operand2) \(symbol) \(operand1)", op2Evaluation.remainingOps, op.precedence) } } case .Variable(let symbol): return (symbol, remainingOps, op.precedence) } } return ("?", ops, Int.max) }
Update: I forgot to check the second operand above, its corrected now …
The description method can ignore the additional return value:
var description: String { ... (current, ops, _) = description(ops) ... }
The evaluation method needs also a tiny adjustment (just tell it to ignore the additional parameter for binary operations) to cope with the adjusted enum:
private func evaluate(ops: [Op]) -> (result: Double?, remainingOps: [Op]) { ... case .BinaryOperation(_, _, let operation): ... }
The missing brackets break our test cases, therefore we need to test for the “new” expected results:
func testDescription() { ... XCTAssertEqual(brain.description, "cos10") ... XCTAssertEqual(brain.description, "โ10 + 3") ... XCTAssertEqual(brain.description, "3 + 5 + 4") ... XCTAssertEqual(brain.description, "โ(3 + โ5) รท 6") ... XCTAssertEqual(brain.description, "โ(3 + 5), cosฯ") ... }
The complete code for extra task #1 is available on GitHub.
Could you please tell me why you chose that
return (String(format: “%g”, operand) , remainingOps, op.precedence)
instead
return (“\(operand)” , remainingOps, op.precedence)
Thank you
It’s a neat trick to remove trailing zeros.
Thank you
By the way what if you put
3
enter
6
enter
9
*
+
or
3
enter
6
+
9
*
Please compare history display…
I don’t know…
Thank you for quick answer and amazing code work
you are right, I forgot a line (or three)
Update: I forgot to check the second operand above, its corrected now โฆ
the update is only on the blog, please update github too ๐
Hi, I think there is an error in the algorithm. If the user types in “1 1 1 + -“, it shows 1-1+1 instead of 1-(1+1).
btw, thanks for your work!
What’s the difference between 1 – 1 + 1 and 1 – (1 + 1)???