Julliard
Julliard

Reputation: 553

why does this variable have to be unwrapped?

I'm on the second lecture of Stanford's IOS 9 developer course. The lecturer is trying to build a calculator. I can't figure out two parts. One:

if pending != nil {
            accumulator = pending!.binaryFunction(pending!.firstOperand, accumulator)
            pending = nil

i understand that pending is an optional. but why is it that i still have to force unwrap it when I nested it in an "IF" statement that checks if it is nil or not?

The second part is:

  private struct PendingBinaryOperationInfo {
        var binaryFunction: (Double, Double) -> Double
        var firstOperand: Double
    }

why do the two variables need not have an initialiser? is it just because it is under "structure"?

The whole code is below. The two parts where i don't understand are found at the bottom of the chunk.

thanks a lot!

import Foundation

class CalculatorBrain
{
    private var accumulator = 0.0

    func setOperand(operand: Double) {
        accumulator = operand
    }

    private var operations: Dictionary<String,Operation> = [
        "e" : Operation.Constant(M_E),
        "√" : Operation.UnaryOperation(sqrt),
        "cos" : Operation.UnaryOperation(cos),
        "×" : Operation.BinaryOperation({ $0 * $1 }),
        "÷" : Operation.BinaryOperation({ $0 / $1 }),
        "+" : Operation.BinaryOperation({ $0 + $1 }),
        "−" : Operation.BinaryOperation({ $0 - $1 }),
        "=" : Operation.Equals
    ]

    private enum Operation {
        case Constant(Double)
        case UnaryOperation ((Double) -> Double)
        case BinaryOperation ((Double, Double) -> Double)
        case Equals
    }

    func performOperation(symbol: String) {
        if let operation = operations[symbol] {
            switch operation {
            case .Constant(let value):
                accumulator = value
            case .UnaryOperation(let function):
                accumulator = function(accumulator)
            case .BinaryOperation(let function):
                executePendingBinaryOperation()
                pending = PendingBinaryOperationInfo(binaryFunction: function, firstOperand: accumulator)
            case .Equals:
                executePendingBinaryOperation()
            }
        }
    }

    private func executePendingBinaryOperation()
    {
        if pending != nil {
            accumulator = pending!.binaryFunction(pending!.firstOperand, accumulator)
            pending = nil
        }
    }

    private var pending: PendingBinaryOperationInfo?

    private struct PendingBinaryOperationInfo {
        var binaryFunction: (Double, Double) -> Double
        var firstOperand: Double
    }

    var result: Double {
        get {
            return accumulator
        }
    }
}

Upvotes: 0

Views: 207

Answers (3)

vacawama
vacawama

Reputation: 154711

if pending != nil {
            accumulator = pending!.binaryFunction(pending!.firstOperand, accumulator)
            pending = nil

why is it that i still have to force unwrap it when I nested it in an "IF" statement that checks if it is nil or not?

Swift is a strongly typed language. In this case, pending is of type PendingBinaryOperationInfo?. Just because you checked it to make sure it isn't nil doesn't change the fact that pending is of type PendingBinaryOperationInfo?. You still need to unwrap it to access the PendingBinaryOperationInfo that is wrapped by the Optional.

The check for nil merely ensures that you can safely unwrap pending with ! because you now know it won't crash due to pending being nil.

Sometimes, programmers will write this like this:

if let unwrappedPending = pending {
    accumulator = unwrappedPending.binaryFunction(unwrappedPending.firstOperand, accumulator)
    pending = nil

This uses optional binding to unwrap pending and assign it to a new variable unwrappedPending that is of type PendingBinaryOperationInfo (which is not Optional).

Sometimes, you might also see this:

if let pending = pending {
    accumulator = pending.binaryFunction(pending.firstOperand, accumulator)
    pending = nil  // Oh, but this won't work because pending isn't optional

In this case, a new variable pending which is of type PendingBinaryOperationInfo is created which hides the original pending variable. I personally don't like this form because it confuses the issue with 2 different variables of two different types with the same name. In this case, it wouldn't work anyway, because the author also wants to set pending to nil, and you no longer have access to the original optional pending inside the if.


The second part is:

private struct PendingBinaryOperationInfo {
    var binaryFunction: (Double, Double) -> Double
    var firstOperand: Double
}

why do the two variables need not have an initialiser? is it just because it is under "structure"?"

Yes. For structures, Swift automatically generates an initializer for you that initializes all properties. You can see this initializer with Xcode's autocomplete feature by typing:

PendingBinaryOperationInfo(

As soon as you type the (, the initializer will pop up as the suggested completion. Here it is in a Playground:

autocompletion example in a Playground

Upvotes: 1

RGPaul
RGPaul

Reputation: 401

The variable accumulator isn't an optional and cannot become nil, thats the reason why you have to force unwrap the pending optional and cannot use the optional chaining question mark(?) which might return nil.

The struct PendingBinaryOperationInfo has no default values and therefor has to be initialized.

Upvotes: 0

sbarow
sbarow

Reputation: 2819

You're checking for nil but the object hasn't been unwrapped, what you can do is something like this

if let pending = pending { ... }

The second part of your question, the variables in that struct are NOT nil therefore when you initialize PendingBinaryOperationInfo you have to initialize it with non-null values, you can get around that by assigning default values in the struct

Upvotes: 0

Related Questions