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
- Swift book
- Basic Operators · Control Flow
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())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")
}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)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)
}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.
var result = 1return result}print(factorial(5))}result *= kfor k in 1...n {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.