cs193p – Project #2 Assignment #2 Extra Task #1

von 3247 [Public domain], via Wikimedia Commons

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.

FacebooktwitterredditpinterestlinkedintumblrmailFacebooktwitterredditpinterestlinkedintumblrmail

7 thoughts on “cs193p – Project #2 Assignment #2 Extra Task #1”

  1. 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

  2. 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

  3. Update: I forgot to check the second operand above, its corrected now โ€ฆ

    the update is only on the blog, please update github too ๐Ÿ˜€

  4. 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!

Leave a Reply

Your email address will not be published.