Search code examples
iosswiftcs193p

iOS 9 Stanford Course in Swift - Lecture 1


I'm currently trying to complete the Swift Course on iTunes U and we are building a calculator. I'm having trouble understanding part of the code.

I added the code below that I thought was relevant from the file.

Here is what confuses me: why does operation(operand) compute the value for the UnaryOperation (i.e. the square root)? I see that when the CalculatorBrain class is called the dictionary is initialized, but when I print the dictionary out I just get something that looks like this: [✕: ✕, -: -, +: +, ⌹: ⌹, √: √]. So where/when does the program compute the square root when I click on the square root button?

Class CalculatorBrain
    {
    private enum Op: Printable
    {
    case Operand(Double)
    case UnaryOperation(String, Double -> Double)
    case BinaryOperation(String, (Double, Double) -> Double)

    var description: String {
        get {
            switch self {
            case .Operand(let operand):
                return "\(operand)"
            case .UnaryOperation(let symbol, _):
                return symbol
            case .BinaryOperation(let symbol, _):
                return symbol
            }
        }
    }
}

private var opStack = [Op]()

private var knownOps = [String: Op]()

init() {
    func learnOp(op: Op) {
        knownOps[op.description] = op

    }
    learnOp(Op.BinaryOperation("✕", *))
    learnOp(Op.BinaryOperation("⌹") { $1 / $0 })
    learnOp(Op.BinaryOperation("+", +))
    learnOp(Op.BinaryOperation("-") { $0 - $1 })
    learnOp(Op.UnaryOperation ("√", sqrt))

}

private func evaluate(ops: [Op]) -> (result: Double?, 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 operandEvaluation = evaluate(remainingOps)
            if let operand = operandEvaluation.result {
                **return (operation(operand), operandEvaluation.remainingOps)**
            }
        // case.BinaryOperation(.....)
        }
    }
    return (nil, ops)
}

 func evaluate() -> Double? {
    let (result, remainder) = evaluate(opStack)
    return result
}

func pushOperand(operand: Double) -> Double? {
    opStack.append(Op.Operand(operand))
    return evaluate()
}

func performOperation(symbol: String) -> Double? {
    if let operation = knownOps[symbol] {
        opStack.append(operation)
    }
   return evaluate()
}

}


Solution

  • The Op enum implements the Printable protocol, which means it has a description: String property. When you print the Dictionary, you are sending [String : Op] to the println function which then tries to print the Op using its description.

    The reason the description of the operators is the same as its key in the Dictionary is because the learnOp(op: Op) function sets the key to be op.description (knownOps[op.description] = op)

    To see the effects of this, you could add a new operator learnOp(Op.UnaryOperation ("@", sqrt)) which will be printed as @:@ inside of the knownOps Dictionary. (And if you add a new button for the @ operator, it will also perform the square root operation)


    Since the calculator is stack based, the operands get pushed on, then the operations. When evaluate() gets called, it calls evaluate(opStack) passing the entire stack through.

    evaluate(ops: [Op]) then takes the to item off of the stack and evaluates the function after having calculated the operands.

    As an example, lets say you want to calucalte sqrt(4 + 5).

    You would push the items onto the stack, and it would look like: [ 4, 5, +, sqrt ]

    Then evaluate(ops: [Op]) sees the sqrt and evaluates the operand with a recursive call. That call then evaluates + with two more recursive calls which return 5 and 4.

    The tree of calls would look like this:

    ops: [4, 5, +, sqrt]    // Returns sqrt(9) = 3
              |
       ops: [4, 5, +]       // Returns 4 + 5 = 9
          ____|_____
         |          |
    ops: [4, 5]  ops: [4]
    return 5     return 4
    

    I strongly recommend you put a breakpoint on the evaluate() -> Double? function and step through the program to see where it goes with different operands and operations.