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.

FacebooktwitterredditpinterestlinkedintumblrmailFacebooktwitterredditpinterestlinkedintumblrmail

4 thoughts on “cs193p – Assignment #2 Task #4”

  1. Hey, Im having some trouble with Assignment 2, especially with the “Do not add, change or remove any public API. Except” I noticed you have added public mutating methods. Is it possible to do it without any public methods/vars with the exception of the two tasks 3 and 4?

    I’ve been stuck on this for 2 days now.

    1. In task #3 I added setOperand as requested. As setter it has to be mutating, otherwise it would be able to set anything.
      In taks #4 I added evaluate as requested, which is not mutating as requested.
      The other methods I added are not part of the public API, they cannot be acccess from outside the class.

      1. You did modify performOperation which is part of public API. They really confused me with with task 1: “Do not add, change or remove any public API. “. Basically all public API is changed when assigment is finished. They wrote that every public method (other than evaluate) can be wrote in single line and asked us to change all public vars using evaluate. I get that, but what did they mean then when they wrote “Do not change, remove or add any public API” ?

      2. I had the same problem with “Do not add, change or remove any public API. Except”. On line 105 I noticed your performOperation method uses the array called stack. Did you always have it like this? My original performOperation method looks very different. Did I miss something?

Leave a Reply

Your email address will not be published.