cs193p – Assignment #2 Task #4

Now that you allow variables to be entered as operands, add a method to evaluate the CalculatorBrain (i.e. calculate its result) by substituting values for those variables found in a supplied Dictionary …

    func evaluate(using variables: Dictionary<String,Double>? = nil)
          -> (result: Double?, isPending: Bool, description: String)

Note that this takes an Optional Dictionary (with Strings as keys and Doubles as values) as its argument and that that argument defaults to nil if not supplied when this method is called. Also note that it returns a tuple (the first element of which is an Optional Double). This method is not mutating and you are not allowed to make it so. If a variable that has been set as an operand is not found in the Dictionary, assume its value is zero.

It happened, it had to happen. In the last task, I just added variables and operands to the operation struct. That was less then optimal. Let’s undo that and create a new struct instead:

    private var stack = [Element]()
    
    private enum Element {
        case operation(String)
        case operand(Double)
        case variable(String)
    }

Entering variables, operands and operations to the model, just add them to the stack:

    mutating func setOperand(_ operand: Double) {
        stack.append(Element.operand(operand))
    }
    
    mutating func setOperand(variable named: String) {
        stack.append(Element.variable(named))
    }
    
    mutating func performOperation(_ symbol: String) {
        stack.append(Element.operation(symbol))
    }

For the result and description, just call the evaluate method:

    var result: Double? {
        return evaluate().result
    }
    
    var resultIsPending: Bool {
        return evaluate().isPending
    }
    
    var description: String? {
        return evaluate().description
    }

Move the accumulator, the “pending” stuff into the evaluate method – we do not need them outside any more:

   func evaluate(using variables: Dictionary<String,Double>? = nil)
        -> (result: Double?, isPending: Bool, description: String)
    {
        var accumulator: (Double, String)?        
        var pendingBinaryOperation: PendingBinaryOperation?        
        struct PendingBinaryOperation {
            ...
        }
        func performPendingBinaryOperation() {
            ...
        }
    }

Hopefully you did not throw away the code for the result and the description, because it also goes into the evaluate method (but be careful if you just copy and past there might be a circular loop hidden in the old code):

   func evaluate(using variables: Dictionary<String,Double>? = nil)
        -> (result: Double?, isPending: Bool, description: String)
    {
        var result: Double? {
            if nil != accumulator {
                return accumulator!.0
            }
            return nil
        }        
        var description: String? {
            if nil != pendingBinaryOperation {
                return pendingBinaryOperation!.description(pendingBinaryOperation!.firstOperand.1, accumulator?.1 ?? "")
            } else {
                return accumulator?.1
            }
        }

        return (result, nil != pendingBinaryOperation, description ?? "")
    }

Finally the interesting part. Loop over the stack. Just set the accumulator for operands and variables. Use the old code of performOperation for the operations:

   func evaluate(using variables: Dictionary<String,Double>? = nil)
        -> (result: Double?, isPending: Bool, description: String)
    {
        ...
        for element in stack {
            switch element {
            case .operand(let value):
                accumulator = (value, "\(value)")
            case .operation(let symbol):
                if let operation = operations[symbol] {
                    ...
                }
            case .variable(let symbol):
                if let value = variables?[symbol] {
                    accumulator = (value, "\(value)")
                } else {
                    accumulator = (0, "0")
                }
            }
        }
        ...
    }

The complete code for the assignment #2 task #4 is available on GitHub.

Facebooktwittergoogle_plusredditpinterestlinkedintumblrmailFacebooktwittergoogle_plusredditpinterestlinkedintumblrmail

Flattr this!

Leave a Reply

Your email address will not be published. Required fields are marked *