Names and functions
Bind names with let and var, define functions with argument labels, and see why a function that prints is different from one that returns.
- CS61A
- Functions · Composing Programs 1.2–1.3
- Swift book
- The Basics · Functions
Last lesson every value was written out in full. Programs get interesting when we can name things: a value we computed once, or a whole computation we want to repeat. Naming is the first means of abstraction.
Binding names
let binds a name to a value. After that, the name stands for the value:
let radius = 10.0
let pi = 3.14159
let area = pi * radius * radius
print(area) // 314.159
A name bound with let is a constant: it cannot be rebound. When a value
genuinely needs to change, use var for a variable:
var total = 0
total = total + 5
total += 5 // shorthand for total = total + 5
print(total) // 10
Prefer let. When a name cannot change, a reader never has to track what
happened to it, and the compiler will tell you if you try.
What would Swift print?
let n = 5
n = 6
print(n)Show answer and explanation
AnswerError
n was bound with let, so n = 6 is a compile-time error: “cannot assign
to value: ‘n’ is a ‘let’ constant”. Nothing prints. Declaring it with var
would print 6.
Types are inferred
let radius = 10.0 never says “this is a Double”; Swift infers it from
the value. You can write the type yourself with an annotation, which is
useful when the value alone would suggest a different type:
let count: Double = 3
print(count / 2) // 1.5, because count is a Double
Defining functions
A function definition gives a name to a computation. It lists the
parameters and their types, the type of the result after ->, and a body:
func square(_ x: Int) -> Int {
return x * x
}
print(square(12)) // 144
print(square(square(3))) // 81
When the body is a single expression, return can be left out:
func square(_ x: Int) -> Int { x * x } means the same thing.
Calling a function follows the rule from last lesson: evaluate the
arguments, then apply. Applying a user-defined function means binding its
parameters to the argument values in a new, local frame, and evaluating
the body there. The parameter x exists only while square runs.
What would Swift print?
func square(_ x: Int) -> Int { x * x }
let x = 3
print(square(x + 1), x)Show answer and explanation
Answer16 3
The argument x + 1 is evaluated first, giving 4, and square binds its own
parameter x to 4 in its own frame. The global x is untouched, so the
second value printed is still 3.
Argument labels
Swift functions have argument labels, names written at the call site. By default the label is the parameter name:
func greet(person: String) -> String {
"Hello, \(person)!"
}
print(greet(person: "Anna"))
You can give a separate label and parameter name, so the call reads like a sentence while the body uses a short name. An underscore means no label:
func divide(_ dividend: Int, by divisor: Int) -> Int {
dividend / divisor
}
print(divide(17, by: 5)) // 3
Labels are part of the function’s name. Leaving one out is an error.
What would Swift print?
func increment(value: Int) -> Int { value + 1 }
print(increment(2))Show answer and explanation
AnswerError
The label is value:, so the call must be increment(value: 2). Swift
reports “missing argument label ‘value:’ in call”.
Returning several values
A function returns one value, but that value can be a tuple. Labels on the tuple’s elements let callers read them by name:
func divide(_ n: Int, by d: Int) -> (quotient: Int, remainder: Int) {
(n / d, n % d)
}
let result = divide(17, by: 5)
print(result) // (quotient: 3, remainder: 2)
print(result.remainder) // 2
Pure functions and print
Some functions only compute a value: square, max, divide. Calling them
twice with the same arguments gives the same result, and nothing else
happens. CS61A calls these pure functions.
print is different. Its job is a side effect, writing to the screen,
and the value it returns is empty. In Swift that empty value is written ()
and its type is called Void.
What would Swift print?
print(print("Hi"))Show answer and explanation
AnswerHi\n()
The argument is evaluated first: the inner print("Hi") writes Hi and
returns (). Then the outer print writes that value, (). This is the
Swift version of a classic CS61A question, where Python prints None.
The difference matters when you build on a function. A function that returns its answer can be used inside a larger expression; one that only prints it cannot.
Put the lines in order
Put the lines in order. The function should return the quotient and remainder; the program should print (3, 2).
let remainder = n - quotient * dlet quotient = n / d}func divide(_ n: Int, by d: Int) -> (Int, Int) {return (quotient, remainder)print(divide(17, by: 5))
Show the correct program
func divide(_ n: Int, by d: Int) -> (Int, Int) {
let quotient = n / d
let remainder = n - quotient * d
return (quotient, remainder)
}
print(divide(17, by: 5))Lab 1: Functions
Write each function in a file lab01.swift, with the assert lines from
the solution below it, and run swift lab01.swift. No output means every
check passed.
Q1: Fahrenheit to Celsius
Write celsius(fromFahrenheit:), which converts a Double temperature
using C = (F − 32) × 5 / 9.
func celsius(fromFahrenheit f: Double) -> Double {
// your code here
}
Show solution
func celsius(fromFahrenheit f: Double) -> Double {
(f - 32) * 5 / 9
}
assert(celsius(fromFahrenheit: 212) == 100)
assert(celsius(fromFahrenheit: 32) == 0)
assert(celsius(fromFahrenheit: -40) == -40)The label fromFahrenheit makes the call read as English, while the body
uses the short parameter name f.
Q2: Two of three
Write twoOfThree(_:_:_:), which takes three positive integers and returns
the sum of the squares of the two smallest. Use a single expression in the
body. min and max may help.
func twoOfThree(_ i: Int, _ j: Int, _ k: Int) -> Int {
// your code here
}
Show solution
func twoOfThree(_ i: Int, _ j: Int, _ k: Int) -> Int {
i * i + j * j + k * k - max(i, j, k) * max(i, j, k)
}
assert(twoOfThree(1, 2, 3) == 5)
assert(twoOfThree(5, 3, 1) == 10)
assert(twoOfThree(10, 2, 8) == 68)
assert(twoOfThree(5, 5, 5) == 50)Adding all three squares and subtracting the largest avoids working out
which two are the smallest. Another single expression is
min(i, j, k) * min(i, j, k) + ..., but finding the middle value is harder
than removing the largest.
Q3: Print or return?
Here are two versions of the same function. Without running them, decide what each program prints. Then run them to check.
func shout(_ word: String) {
print(word.uppercased())
}
func shouted(_ word: String) -> String {
word.uppercased()
}
shout("hi")
print(shouted("hi") + "!")
Show solution
It prints HI and then HI!. shout writes the result itself and returns
nothing, so it cannot be used inside +. shouted returns its result, so
the caller can decide what to do with it: add an exclamation mark, compare
it, or print it. Prefer the returning version unless printing is the
function’s whole job.
What’s next
Next lesson: choosing between options with if and switch, and repeating
work with loops.