More Swift – A minor refactor

In my earlier I post I talked about not being very sure that a method I wrote for one of the Stanford iOS Programming course assignments was really embracing Swift as a language. I then posted about using Swift’s unit test framework that would make any refactoring easier and better.

And so on to the refactoring…

Well, with a fresh day came a fresh eye, and there was not too much I could really do to make the method more Swift than C/C++. I ended up mainly folding some let statements into inline expressions. Not exactly hard, but when all was completed I think the code is more or less as tight as it could be and still easily readable – opinions to the contrary are very welcome.

Here’s the revised method (see one of articles linked to above for the original code):

 
    private func formatDescription(ops: [Op]) -> (desc: String?, remainingOps: [Op]) {
        if !ops.isEmpty {
            var remainingOps = ops
            let op = remainingOps.removeLast()
            switch op {
            case .Operand(let operand):
                return ("\(operand)", remainingOps)
            case .UnaryOperation(let operation,_):
                let (op1Text, remainingOps1) = formatDescription(remainingOps)
                let returnText = "\(operation)(\(op1Text ?? "?"))"
                return (returnText, remainingOps1)
            case .BinaryOperation(let operation,_):
                let (op1Text, remainingOps1) = formatDescription(remainingOps)
                let (op2Text, remainingOps2) = formatDescription(remainingOps1)
                let returnText = "\(op2Text ?? "?") \(operation) \(op1Text ?? "?")"
                return (returnText, remainingOps2)
            case .Variable(let variable):
                return (variable, remainingOps)
            case .Constant(let constant):
                return (constant, remainingOps)
            }
        }
        return (nil, ops)
    }

Because of the unit tests written before, it was trivial to ensure the before and after are functionally equivalent.

I did end up adding a handful of extra unit tests to ensure cases where an operand was missing was correctly handled:

 

    func testDescriptionMissingOperand() {
        brain.performOperation("cos")
        XCTAssert(brain.description == "cos(?)")
    }

    func testDescriptionMissingOperand2() {
        brain.pushOperand(4)
        brain.performOperation("+")
        XCTAssert(brain.description == "? + 4.0")
    }

Note that the code as presented does not yet implement the whole assignment. The main piece missing is adding the formatting of expressions correctly with brackets by considering operator precedence.

Leave a Reply