Tutorial outline

Unit 1

  1. 01Getting started
  2. 02Names and functions
  3. 03Control
  4. 04Higher-order functions
  5. 05Environments and closures
  6. 06Functional abstraction
  7. 07Recursion
  8. 08Tree recursion

Unit 2

  1. 09Sequences
  2. 10Dictionaries, sets, and tuples
  3. 11Optionals
  4. 12Structures and enumerations
  5. 13Linked lists and trees
  6. 14Mutation and value semantics
  7. 15Classes and inheritance
  8. 16Protocols and generics
  9. 17Iterators and lazy sequences
  10. 18Efficiency and memoization

Unit 3

  1. 19Algebraic data types and pattern matching
  2. 20Writing an interpreter
  3. 21Error handling
  4. 22Concurrency with async and await
  5. 23Testing
  6. 24Building a Swift package

Control

Booleans and short-circuiting, if and switch as statements and expressions, and repeating work with while and for-in loops.

CS61A
Control · Composing Programs 1.4–1.5

So far every program has run straight from top to bottom. Control lets a program choose what to do next and repeat work until it is done.

Boolean values

Comparisons produce a Bool, either true or false:

print(3 < 5)        // true
print(3 == 3.0)     // true
print("a" != "b")   // true

&& (and), || (or), and ! (not) combine them. Like Python’s and and or, && and || short-circuit: they stop as soon as the answer is known, and never evaluate the right side if they do not need it.

What would Swift print?

func loud() -> Bool {
    print("called")
    return true
}
print(false && loud())
print(true || loud())

Type Error if running it would crash. For several lines of output, put each on its own line.

Show answer and explanation

Answerfalse\ntrue

false && … is false no matter what comes after, so loud() is never called. The same goes for true || …. Nothing but the two results is printed.

Short-circuiting is useful for guarding a risky check: count > 0 && total / count > 10 never divides by zero.

Unlike Python, Swift has no “truthy” values. A condition must be a Bool: if 1 { … } is a compile-time error.

Conditional statements

An if statement runs a block only when its condition is true, with optional else if and else clauses. Braces are required, parentheses around the condition are not.

func describe(_ temperature: Int) -> String {
    if temperature < 0 {
        return "freezing"
    } else if temperature < 20 {
        return "cool"
    } else {
        return "warm"
    }
}
print(describe(-5), describe(12), describe(28))   // freezing cool warm

In Swift, if can also be an expression that produces a value, as long as every branch is a single expression of the same type:

let x = -4
let sign = if x < 0 { "negative" } else if x == 0 { "zero" } else { "positive" }
print(sign)   // negative

switch

switch compares one value against a list of patterns. Patterns can be single values, lists of values, or ranges:

let grade = 87
switch grade {
case 90...100:
    print("A")
case 80..<90:
    print("B")
default:
    print("C or below")
}

90...100 is a closed range that includes 100; 80..<90 stops before 90. There is no fall-through between cases, so no break is needed. Swift also insists that a switch is exhaustive: every possible value must match some case.

What would Swift print?

let n = 3
switch n {
case 1:
    print("one")
case 2:
    print("two")
}

Type Error if running it would crash. For several lines of output, put each on its own line.

Show answer and explanation

AnswerError

An Int could be any number, and nothing handles 3 or anything else. “switch must be exhaustive” is a compile-time error, so nothing runs. Adding a default: case fixes it.

Iteration

A while loop repeats its body as long as its condition is true. This is the CS61A example of computing Fibonacci numbers by walking forward:

func fib(_ n: Int) -> Int {
    var previous = 0
    var current = 1
    var k = 1
    while k < n {
        (previous, current) = (current, previous + current)
        k += 1
    }
    return n == 0 ? 0 : current
}
print(fib(10))   // 55

Assigning to a tuple updates both names at once, using the old values on the right. The ? : operator is a compact conditional expression: a ? b : c is b when a is true and c otherwise.

What would Swift print?

var i = 0
var total = 0
while i < 4 {
    i += 1
    total += i
}
print(i, total)

Type Error if running it would crash. For several lines of output, put each on its own line.

Show answer and explanation

Answer4 10

The loop runs with i becoming 1, 2, 3, and 4, adding each to total, so total is 10. When i is 4 the condition fails and the loop ends.

A for-in loop runs once for each element of a sequence, such as a range:

for k in 1...3 {
    print(k * k)
}
for k in stride(from: 10, to: 0, by: -3) {
    print(k, terminator: " ")   // 10 7 4 1
}
print()

terminator: replaces the newline that print normally adds. When you do not need the loop variable, write _: for _ in 1...3 { … }.

What would Swift print?

for k in 1..<4 {
    print(k, terminator: " ")
}
print()
for k in 1...4 where k % 2 == 0 {
    print(k)
}

Type Error if running it would crash. For several lines of output, put each on its own line.

Show answer and explanation

Answer1 2 3\n2\n4

1..<4 stops before 4, so the first loop prints 1 2 3 on one line. A where clause filters the second loop to even values of k.

Put the lines in order

Put the lines in order so factorial(5) prints 120.

  1. var result = 1
  2. return result
  3. }
  4. print(factorial(5))
  5. }
  6. result *= k
  7. for k in 1...n {
  8. func factorial(_ n: Int) -> Int {

Show the correct program
func factorial(_ n: Int) -> Int {
    var result = 1
    for k in 1...n {
        result *= k
    }
    return result
}
print(factorial(5))

Lab 2: Control

Put your solutions in lab02.swift with the assert lines, and run it.

Q1: Falling factorial

fallingFactorial(n, k) multiplies the k consecutive numbers counting down from n: fallingFactorial(6, 3) is 6 × 5 × 4 = 120. When k is 0 the result is 1.

func fallingFactorial(_ n: Int, _ k: Int) -> Int {
    // your code here
}
Show solution
func fallingFactorial(_ n: Int, _ k: Int) -> Int {
    var total = 1
    var factor = n
    for _ in 0..<k {
        total *= factor
        factor -= 1
    }
    return total
}

assert(fallingFactorial(6, 3) == 120)
assert(fallingFactorial(4, 3) == 24)
assert(fallingFactorial(4, 1) == 4)
assert(fallingFactorial(4, 0) == 1)

0..<k runs the loop exactly k times, and zero times when k is 0, which leaves total at 1.

Q2: Sum of digits

Return the sum of the decimal digits of a non-negative integer. Use % 10 to get the last digit and / 10 to remove it.

func sumDigits(_ n: Int) -> Int {
    // your code here
}
Show solution
func sumDigits(_ n: Int) -> Int {
    var rest = n
    var total = 0
    while rest > 0 {
        total += rest % 10
        rest /= 10
    }
    return total
}

assert(sumDigits(10) == 1)
assert(sumDigits(4224) == 12)
assert(sumDigits(1234567890) == 45)
assert(sumDigits(0) == 0)

Q3: Double eights

Return true if a non-negative integer contains two 8s next to each other.

func doubleEights(_ n: Int) -> Bool {
    // your code here
}
Show solution
func doubleEights(_ n: Int) -> Bool {
    var rest = n
    var previous = -1
    while rest > 0 {
        let digit = rest % 10
        if digit == 8 && previous == 8 {
            return true
        }
        previous = digit
        rest /= 10
    }
    return false
}

assert(!doubleEights(8))
assert(doubleEights(88))
assert(doubleEights(2882))
assert(doubleEights(880088))
assert(!doubleEights(12345))
assert(!doubleEights(80808080))

return true inside the loop leaves the function immediately, as soon as the answer is known.

Q4: a plus the absolute value of b

In Swift, operators are functions too: + has type (Int, Int) -> Int. Fill in the blank so aPlusAbsB returns a + |b| without calling abs.

func aPlusAbsB(_ a: Int, _ b: Int) -> Int {
    let f: (Int, Int) -> Int = ______
    return f(a, b)
}
Show solution
func aPlusAbsB(_ a: Int, _ b: Int) -> Int {
    let f: (Int, Int) -> Int = b < 0 ? (-) : (+)
    return f(a, b)
}

assert(aPlusAbsB(2, 3) == 5)
assert(aPlusAbsB(2, -3) == 5)
assert(aPlusAbsB(-1, 4) == 3)
assert(aPlusAbsB(-1, -4) == 3)

When b is negative, subtracting it adds its absolute value. Wrapping an operator in parentheses, (-), refers to the operator’s function instead of using it. This is a preview of the next lesson, where functions are values.

What’s next

Next lesson: functions that take functions as arguments and return them as results.