Swift Programming from Scratch
The Swift Sandbox is integrated, making the exercises interactive. Read more about the book here.
Chapter 7: Functions
A function is a chunk of code that performs a specific task. Functions have a name that describes their purpose, that name is used to call the function to perform the task when needed. You can provide data to a function by sending parameters to it, and the function can give data back as result.
Let’s take a simple example:
func isOdd(number: Int) -> Bool {
if number % 2 == 1 {
return true
} else {
return false
}
}
isOdd(number: 1) // true
isOdd(number: 2) // false
isOdd(number: 3) // true
In the above example isOdd
is the name of the function, number
is the parameter and Bool
is the return type.
Defining a function
When you define a function, you can optionally define one or more named, typed values that the function takes as input (known as parameters), and/or a type of value that the function will pass back as output when it is done (known as its return type).
The general syntax for a function is:
func name
(list of parameters
) -> return type
{
statements
}
Some functions don’t return any values. In that case the syntax doesn’t have the arrow(->) and the return type.
func name
(list of parameters
) {
statements
}
Functions with no parameters with no return value
func sayHello() {
print("Hello!")
}
sayHello() // Hello!
Functions with one parameter with no return value
Parameters are followed by their type.
func sayHello(to name: String) {
print("Hello \(name)!")
}
sayHello(to: "Swift") // Hello Swift!
Functions with one parameter and return value
To add a return value to a function write ->
after the list of parameter followed by the type of the result. Functions that return a value must do so using the return
keyword. When calling return inside a function the code execution will stop at that line – similar to the break
statement inside a loop.
func square(number: Int) -> Int {
return number * number
}
square(number: 1) // 1
square(number: 2) // 4
square(number: 3) // 9
Functions with multiples parameters with no return value
To declare multiple parameters use commas to separate them.
func count(from: Int, to: Int) {
for i in from...to {
print(i)
}
}
count(from: 5, to: 10)
// 5
// 6
// 7
// 8
// 9
// 10
Notice that the all parameters have the name in the function call. That is called the external parameter name. All parameters have an implicit external parameter name, the same as the local parameter name.
Functions with multiples parameters and return value
func sum(_ a: Int, _ b: Int) -> Int {
return a + b
}
print(sum(1, 2)) // 3
Notice that this time neither parameter appeared in the function call. This is because of the _
character in front ofa
and b
. It this case _
means don’t give this parameter an external name. Remember this because you are going to use it in the exercises.
External parameter names
Sometimes it’s useful to name your parameters differently when you call a function.
For example:
func sayHello(name:String) {
print("Hello " + name + "!")
}
sayHello(name: "Batman")
// Hello Batman!
In this case it would have more sense name the parameter to
because then the function call would readsayHello(to: "Batman")
. But then the code inse the function would make less sense.
To do this you must define external parameter names for them. You can write external parameter names before the local name. All parameters have the external parameter name set to the local one by default. You can change it by writing a different name before it.
func sayHello(to name:String) {
print("Hello " + name + "!")
}
sayHello(to: "Batman")
// Hello Batman!
You can make a function ignore the external parameter name by writing _
in front of the parameter name:
func double(_ number: Int) -> Int {
return number * 2
}
External parameter names make your code clear. Don’t remove them unless you have to.
Default Parameter Values
You can define a default value for any parameter in a function definition. You do this by following the parameter definition with a =
sign and the value for that parameter. If a parameter has a default value set you can omit that parameter when calling the function. To keep things clean it’s recommended that you write all the parameters with default value at the end of the parameter list.
func countdown(from: Int, to: Int = 1) {
for i in (to...from).reversed() {
print(i)
}
}
countdown(from: 3)
// 3
// 2
// 1
countdown(from: 5, to: 3)
// 5
// 4
// 3
In-Out Parameters
Function parameters are constant by default, that means that you cannot change the value of a parameter inside a function. Trying to change the value of a parameter will result in a compile error.
If you want the function to change the value of a parameter and you want those changes to persist after the function call, define the parameter as an inout
parameter.
Keep in mind that you can only pass variables as in-out parameters. You cannot pass a constant or a literal value, because they cannot be changed. You have to write an ampersand (&
) in front of the variable name when calling the function. That will indicate that the variable can be modified by the function.
func double(number: inout Int) {
number = number * 2
}
var n = 10
double(number: &n)
print(n) // 20
Quick tip
If you want to change the value of a parameter and those changes don’t need to be reflected outside the function then you can just declare a variable with the same name:func printNumber(after number: Int) { var number = number number += 1 print(number) } printNumber(after: 2) // 3
7.1 Min
Write a function named min2
that takes two Int
values, a
and b
, and returns the smallest one. Use _
to ignore the external parameter names for both a
and b
.
func min2(_ a: Int, _ b: Int) -> Int
Function call:
min2(1,2)
Output:
1
Function call:
min2(10,5)
Output:
5
You can have multiple return
statements in one function.
func min2(_ a: Int, _ b: Int) -> Int {
if a < b {
return a
} else {
return b
}
}
7.2 Last Digit
Write a function that takes an Int
and returns it’s last digit. Name the function lastDigit
. Use _
to ignore the external parameter name.
func lastDigit(_ number: Int) -> Int
Function call:
lastDigit(12345)
Output:
5
Function call:
lastDigit(1000)
Output:
0
Function call:
lastDigit(123)
Output:
3
Use the modulo(%
) operator.
func lastDigit(_ number: Int) -> Int {
return number % 10
}
7.3 First Numbers
Write a function named first
that takes an Int
named N
and returns an array with the first N
numbers starting from 1
. Use _
to ignore the external parameter name.
func first(_ N: Int) -> [Int]
Function call:
first(3)
Output:
[1, 2, 3]
Function call:
first(1)
Output:
[1]
Function call:
first(10)
Output:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Use the append
function on arrays to create the required result.
func first(_ N: Int) -> [Int] {
var numbers:[Int] = []
for number in 1...N {
numbers.append(number)
}
return numbers
}
7.4 Countdown
Write a function named countdown
that takes a number N
. The function should print the numbers from N
to 1
with a one second pause in between and then write GO!
in the end. To make the computer wait for one second call thesleep
function from the standard library. The sleep
function takes one parameter, the number of seconds to sleep.
In order to use the sleep
function you will need to import the Foundation framework.
import Foundation
// now you can use the sleep function
sleep(1) //will wait for one second before executing the next line
func countdown(_ N: Int)
import Foundation
// your code here
Function call:
coutdown(3)
Output:
3
2
1
GO!
import Foundation
func countdown(_ N: Int) {
var i = N
while i > 0 {
print(i)
sleep(1)
i -= 1
}
print("GO!")
}
7.5 Prime Numbers
Implement the following functions. The divides
function returns true
if a
is divisible by b
and false
otherwise. The countDivisors
function should use the divides
function to return the number of divisors of number
. TheisPrime
function should use the countDivisors
function to determine if number
is prime.
func divides(_ a: Int, _ b: Int) -> Bool
func countDivisors(_ number: Int) -> Int
func isPrime(_ number: Int) -> Bool
Examples:
divides(3, 2) // false - 3 is not divisible by 2
divides(6, 3) // true - 6 is divisible by 3
countDivisors(2) // 2 - 1 and 2
countDivisors(6) // 4 - 1, 2, 3 and 6
countDivisors(12) // 6 - 1, 2, 3, 4, 6 and 12
isPrime(2) // true
isPrime(3) // true
isPrime(10) // false
isPrime(13) // true
Function call:
isPrime(2)
Output:
true
Function call:
isPrime(3)
Output:
true
Function call:
isPrime(10)
Output:
false
Function call:
isPrime(13)
Output:
true
The isPrime
function can be implemented in a single line using the countDivisors
function.
func divides(_ a: Int, _ b: Int) -> Bool {
return a % b == 0
}
func countDivisors(_ number: Int) -> Int {
var cnt = 0
for i in 1...number {
if divides(number, i) {
cnt += 1
}
}
return cnt
}
func isPrime(_ number: Int) -> Bool {
return countDivisors(number) == 2
}
7.6 First Primes
Using isPrime
write a function named printFirstPrimes
that takes a parameter named count
of type Int
that prints the first count
prime numbers.
func printFirstPrimes(_ count: Int)
Function call:
printFirstPrimes(3)
Output:
2
3
5
Function call:
printFirstPrimes(10)
Output:
2
3
5
7
11
13
17
19
23
29
Use the isPrime
function from the previous exercise.
func printFirstPrimes(_ count: Int) {
var i = 2
var printed = 0
while printed < count {
if isPrime(i) {
print(i)
++printed
}
++i
}
}
7.7 Repeat Print
Implement a function named repeatPrint
that takes a string message
and a integer count
as parameters. The function should print the message
count
times and then print a newline.
func repeatPrint(message: String, count: Int)
Function call:
repeatPrint("+", 10)
Output:
++++++++++
Function call:
repeatPrint(message: "<->", count: 3)
Output:
<-><-><->
Don’t forget about the newline at the end.
func repeatPrint(message: String, count: Int) {
for i in 1...count {
print(message, terminator: "")
}
print("")
}
7.8 Reverse
Write a function named reverse
that takes an array of integers named numbers
as a parameter. The function should return an array with the numbers from numbers
in reverse order.
func reverse(_ numbers: [Int]) -> [Int]
Function call:
reverse([1, 2, 3])
Output:
[3, 2, 1]
Function call:
reverse([1, 2, 1, 2, 1, 2])
Output:
[2, 1, 2, 1, 2, 1]
func reverse(_ numbers: [Int]) -> [Int] {
var reversed: [Int] = []
for number in numbers {
reversed.insert(number, at: 0)
}
return reversed
}
7.9 Sum
Write a function named sum
that takes an array of integers and returns their sum.
func sum(_ numbers: [Int]) -> Int
Function call:
sum([1, 2, 3])
Output:
6
Function call:
sum([1, 1, 1, 1, 1])
Output:
5
func sum(_ numbers: [Int]) -> Int {
var sum = 0
for number in numbers {
sum += number
}
return sum
}
7.10 Parse number
Write a function named parse(digit:)
that takes a string with one character as parameter. The function should return -1
if the input is not a digit character and the digit otherwise.
parse(digit: "1") // 1
parse(digit: "3") // 3
parse(digit: "a") // -1
func parse(digit: String) -> Int
Use a string of digits let digits = "0123456789"
.
First we check if the given string is a number, if it is not we return -1
. Next we initialize our result to 0
. For each character in the given string we multiply the result by 10, shifting all digits with 1 position to the left and we add the result of parseDigit
for the current digit.
func parse(digit: String) -> Int {
let digits = "0123456789"
var result = 0
for character in digits.characters {
var d = "\(character)"
if d == digit {
return result
}
result += 1
}
return -1
}
Using the parse(digit:)
function you can determine if a string of length one is a digit or not. Implement a function named isNumber
that takes an arbitrary length string and return true
if the string contains only digits and false
otherwise. Note that empty strings should not be considered numbers.
isNumber("a") // false
isNumber("1") // true
isNumber("1234567890") // true
isNumber("12345abc") // false
isNumber("") // false
func isNumber(_ string: String) -> Bool
If the string we’re given is empty we return false
otherwise we iterate through all the characters in our string, if any of these characters returns -1
from our parseDigit
function we return false
. If none of them return -1
from parseDigit it means that all characters in our string are digits and we return true
func isNumber(_ string: String) -> Bool {
if string.characters.count == 0 {
return false
}
for character in string.characters {
if parse(digit: "\(character)") == -1 {
return false
}
}
return true
}
Using the isNumber
and parse(digit:)
functions, write a function named parse(number:)
that takes a string and returns it’s values as an integer or -1
if the string does not contain only digits.
parse(number: "1") // 1
parse(number: "54321") // 54321
parse(number: "1337") // 1337
parse(number: "12cd") // -1
func parse(number: String) -> Int
First we check if the given string is a number, if it is not we return -1
. Next we initialize our result to 0
. For each character in the given string we multiply the result by 10, shifting all digits with 1 position to the left and we add the result of parse(digit:)
for the current digit.
func parse(number: String) -> Int {
if isNumber(number) != true {
return -1
}
var result = 0
for character in number.characters {
var digit = "\(character)"
result = result * 10 + parse(digit: digit)
}
return result
}
7.11 Time Difference
Write a function named timeDifference
. It takes as input four numbers that represent two times in a day and returns the difference in minutes between them. The first two parameters firstHour
and firstMinute
represent the hour and minute of the first time. The last two secondHour
and secondMinute
represent the hour and minute of the second time. All parameters should have external parameter names with the same name as the local ones.
func timeDifference(firstHour: Int,
firstMinute: Int,
secondHour: Int,
secondMinute: Int) -> Int
Function call:
timeDifference(firstHour: 12, firstMinute: 3, secondHour: 13, secondMinute: 10)
Output:
67
Function call:
timeDifference(firstHour: 8, firstMinute: 10, secondHour: 17, secondMinute: 30)
Output:
560
You’ll have to handle the case when the difference between minutes is less than 0
.
func timeDifference(firstHour: Int,
firstMinute: Int,
secondHour: Int,
secondMinute: Int) -> Int {
var hourDifference = secondHour - firstHour
var minuteDifference = secondMinute - firstMinute
if minuteDifference < 0 {
hourDifference -= 1
minuteDifference += 60
}
return hourDifference * 60 + minuteDifference
}
7.12 Correct Pairs
Write a function named verify
that takes a string expression
of open and closed parentheses ((
, )
) and returnstrue
if they are correctly paired and false
otherwise.
func verify(expression: String) -> Bool
Function call:
verify(expression: "()")
Output:
true
Function call:
verify(expression: "((")
Output:
false
Function call:
verify(expression: "(())")
Output:
true
Function call:
verify(expression: "()()")
Output:
true
Function call:
verify(expression: "(()))")
Output:
false
Function call:
verify(expression: ")(")
Output:
false
Keep track of how many open parentheses you’ve encountered and how many closed parentheses.
In a correct pairing the number of closed parentheses you encounter can never be greater than the number of open parentheses.
func verify(expression: String) -> Bool {
var open = 0
var closed = 0
for char in expression.characters {
var character = "\(char)"
if character == "(" {
open += 1
} else {
closed += 1
if closed > open {
return false
}
}
}
return open == closed
}
7.13 Mario
Mario uses energy points to walk and jump. He can jump maximum maxJump
meters up or down. You have the height of each 1 meter portion of a level in the heights
array. Determine if Mario can finish the level and how much energy he needs to do it. Mario uses 1 energy point to walk one meter and 2 * jumpHeight
energy points to jumpHeight
meters. Write a function named levelCost
that takes heights
and maxJump
as parameters and returns -1
if Mario cannot finish the level or the total energy cost that he would need to finish the level.
In the beginning Mario will be on the first 1 meter section of the level and the heights
array will always have more than one element. All heights have a value greater or equal to 1.
levelCost(heights: [1, 1, 2, 2, 5, 2, 1, 1], maxJump: 3) // 19
// 1 point to walk
// 2 to jump from 1 to 2
// 1 point to walk
// 6 to jump from 2 to 5
// 6 to jump from 5 to 2
// 2 to jump from 2 to 1
// 1 point to walk
levelCost(heights: [1, 1, 3, 1, 1], maxJump: 2) // 10
// 1 point to walk
// 4 to jump from 1 to 3
// 4 to jump from 3 to 1
// 1 point to walk
levelCost(heights: [1, 1, 8, 1], maxJump: 5) // -1
// Mario cannot jump from 1 to 8
func levelCost(heights: [Int], maxJump: Int) -> Int
Think about how you can compute the energy required for a single step.
func levelCost(heights: [Int], maxJump: Int) -> Int {
var totalEnergy = 0
var lastHeight = 0
for height in heights {
if lastHeight == 0 {
lastHeight = height
} else {
var jumpHeight = lastHeight - height
if jumpHeight < 0 {
jumpHeight = -jumpHeight
}
if jumpHeight > maxJump {
return -1
}
if jumpHeight == 0 {
totalEnergy += 1
} else {
totalEnergy += 2 * jumpHeight
}
lastHeight = height
}
}
return totalEnergy
}
7.14 Queue
A queue is a data structure that can perform two operations:
- push which takes a value and adds it at the end of the queue
- pop which returns the value from the start of the queue and removes it from the queue
Your task is to implement the push
and pop
operations. The most simple way to represent a queue is using an array. Here are some example operations.
// here we define an empty queue
var queue: [Int] = []
// add 1 in the queue
push(1, &queue) // queue = [1]
// add 2 in the queue
push(2, &queue) // queue = [1, 2]
// pop the first element
pop(&queue) // 1, queue = [2, 3]
// add 3 in the queue
push(3, &queue) // queue = [2, 3]
// pop the first element
pop(&queue) // 2, queue = [3]
// pop the first element
pop(&queue) // 3, queue = []
// pop the first element
pop(&queue) // returns nil because there are no elements in the queue
// queue = []
The push
function should take two parameters, the number
and the queue
as an inout parameter.
func push(_ number: Int, _ queue: inout [Int])
The pop
function should take queue
as an inout parameter and return the first number from the queue after removing it. If the queue is empty it should return nil – the result type should be an optional integer(Int?
).
func pop(_ queue: inout [Int]) -> Int?
For the pop
function you’ll have to retrieve the first element in the queue.
func push(_ number: Int, _ queue: inout [Int]) {
queue.append(number)
}
func pop(_ queue: inout [Int]) -> Int? {
var result = queue.first
if queue.count > 0 {
queue.remove(at: 0)
}
return result
}
7.15 Stack
A stack is a data structure that can perform three operations:
- push adds a value on the top of the stack
- top returns the value from the top of the stack
- pop returns the value from the top of the stack and removes it from there
Your task is to implement the push
, top
and pop
operations. The most simple way to represent a stack is using an array. Here are some example operations.
var stack: [Int] = []
push(1, &stack) // stack = [1]
push(2, &stack) // stack = [1, 2]
pop(&stack) // 2, stack = [1]
push(3, &stack) // stack = [1, 3]
pop(&stack) // 3, stack = [1]
pop(&stack) // 1, stack = []
pop(&stack) // returns nil because there are no elements in the stack
// stack = []
push
takes two parameters, the number
that will be pushed and the stack
as an inout parameter.
func push(_ number: Int, _ stack: inout [Int])
top
takes one parameter, the stack
, and returns the value of the top element or nil if the stack is empty – the result type should be and optional integer(Int?
)
func top(_ stack: [Int]) -> Int?
pop
takes the stack
as an inout parameter, and returns the value of the top element after it removes it. If thestack
is empty it should return nil – the result type should be and optional integer(Int?
)
func pop(_ stack: inout [Int]) -> Int?
You’ll have to get the last element from the stack for the top
operation.
func push(_ number: Int, _ stack: inout [Int]) {
stack.append(number)
}
func top(_ stack: [Int]) -> Int? {
if stack.count == 0 {
return nil
}
return stack[stack.count - 1]
}
func pop(_ stack: inout [Int]) -> Int? {
var result = top(stack)
if stack.count > 0 {
stack.remove(at: stack.count - 1)
}
return result
}
Swift Programming from Scratch
Read more about the book here.
Feel free to ask any questions in the comments bellow.
Hello. Question, regarding the solution for 7.6. Does it make a difference whether we write the ++ before or after printed and/or i? Thanks
Just a few notes I made along the way
Time Difference –
This seems unnecessary AFAIK :
if minuteDifference < 0 {
hourDifference -= 1
minuteDifference += 60
}
Mario –
There’s a typo, it’s 10, not 14, as explained in the comments of the exemple.
levelCost(heights: [1, 1, 3, 1, 1], maxJump: 2) // 14
// 1 point to walk
// 4 to jump from 1 to 3
// 4 to jump from 3 to 1
// 1 point to walk
Queue –
Typos :
• push witch takes a value and adds it at the end of the queue
• push witch returns the value from the start of the queue and removes it from the queue
The pop function should the queue as an inout parameter
if minuteDifference < 0 { hourDifference -= 1 minuteDifference += 60 }from 10:45 to 11:10 that branch is usedThank you for the typos
I’m not saying it’s not used, but that you don’t need it.
Having a negative minuteDifference does not break your code.
With your example :
firstHour = 10
firstMinute = 45
secondHour = 11
secondMinute = 10
hourDifference = 11 – 10 = 1
minuteDifference = 10 – 45 = -35
returned value :
hourDifference * 60 + minuteDifference = 1 * 60 + (-35) = 60 – 35 = 25.
Might I humbly suggest that this would be a slightly better solution to 7.2:
func lastDigit(number: Int) -> Int {
return abs(number) % 10
}
hello,
Solution 7.8
func reverse(numbers: [Int]) -> [Int] {
var reversed: [Int] = []
for i in number {
reversed.insert(i, atIndex: 0)
}
return reversed
}
I’m confused why the array appears in reversed. from what i read we iterate reverse and input in the empty array, starting at index 0, how come it appears reverse if we never changed the order of array.
thanks for explaining
The code iterates the numbers array. Reversed is initially empty. At each iteration we insert the current element at index 0 (at the front of the array) in reversed.
Let’s say numbers = [1, 2, 3, 4] reversed will be [1], [2,1], [3,2,1], [4,3,2,1] respectively at each iteration.
thanks for clarifying !
solution 7.12
func verifyParentheses(expression: String) -> Bool {
var open = 0
var closed = 0
for scalar in expression.unicodeScalars {
var character = “\(scalar)”
if character == “(” {
++open
} else {
++closed
if closed > open {
return false
}
}
}
return open == closed
}
code
//if character == “(” {
// ++open
does this syntax mean for ever “(” increase “var open” by 1 ?
also
//if closed > open {
// return false
even in the case where closed < open it returns false, how come swift returns false? this isn't mentioned anywhere in the code
also
// return open == closed
does the word "return" represent something? is this code a shorthand for
// if open == closed { return true}
this really intrigues me since it's not the first time i see a solution with the word "return" pop up at the end and it holds a value when nothing was ever assigned to it.
please explain, really confused :$
For 7.5 you don’t consider 1 as prime, since your condition in ‘isPrime’ equals to 2 and 1 con only be divided by itself.
1 is not prime.
The more you know! You are totally correct!, As I’m reading some math papers explaining why. Thanks for your quick response.
C style of ‘For’ loop is going to be deprecated in future versions of SWIFT, how are we going to write it to behave in decrements instead of increments?
for i in 5.stride(to: 1, by: -1) {
print(i)
}
// 5, 4, 3, 2
for i in 5.stride(through: 1, by: -1) {
print(i)
}
// 5, 4, 3, 2, 1
Hello,
For exercise 7.11 Time Difference,
wouldn’t be simpler in one line:
return (secondHour * 60 + secondMinute) – (firstHour * 60 + firstMinute)
Your solution is also correct and it might look simpler for most coders. As far as I’ve noticed new coders are a bit intimidated by long expressions like that one. Code that has 1-2 instructions per line is easier to understand.
I’m getting an error on 7.13. It says my function definition is not correct, but it is exactly like the one on this page.