If one were to remove all of the names and labels from a Swift function signature, they would be left with the type of the function.
func shoppingList(item: String, quantity: Int) -> String {
"You need to buy \(quantity) \(item)."
}
// shoppingList: (String, Int) -> String
func repeater(_ phrase: String, times: Int) -> String {
var result = ""
for _ in 1...times {
result += phrase
}
return result
}
// repeater: (String, Int) -> String
func printString(line: String) {
print(line)
}
// printString: (String) -> ()
Notice that the types of the first two functions are the same, (String, Int) -> String
, even though the two signatures are different and the two functions work very differently. They both take a String
and an Int
as input and return a String
. And, as one can see from the third function's type, even though the function body doesn't appear to return anything, it actually implicitly returns ()
, the only value of type Void
.
These function types can be used in the same manner as any other type in Swift, for example, they can be used in the declaration of variables or constants. And since functions are values in Swift, already existing functions can be assigned to these constants/variables:
var stringAndIntToString: (String, Int) -> String
stringAndIntToString = shoppingList
stringAndIntToString("carrots", 3)
// => "You need to buy 3 carrots."
As with other variables/constants, the value you assign to it must be of the correct type:
stringAndIntToString = printShoppingList
// Error: Cannot assign value of type '(String) -> ()' to type '(String, Int) -> String'
As function types can be used anywhere other types can be used, it follows that they can be used as parameter types and passed into other functions and called from within those functions.
func apply3(to str: String, function f: (String, Int) -> String) -> String {
f(str,3)
}
// apply3: ([String], (String, Int) -> String) -> [String]
apply3(to: "eggs", function: shoppingList)
// => "You need to buy 3 eggs."
apply3(to: "eggs", function: repeater)
// => "eggseggseggs"
Similarly, function types may be used as return types for functions. In other words, one can write functions that create and return other functions. When creating these functions, the function that is returned can use the other parameters passed into the function or the local variables created inside the parent function.
func makeAdder(base: Int) -> (Int) -> Int {
func adder(_ i: Int) -> Int {
base + i
}
return adder
}
// makeAdder: (Int) -> (Int) -> Int
let add10 = makeAdder(base: 10)
// add10: (Int) -> Int
let subtract20 = makeAdder(base: -20)
// subtract10: (Int) -> Int
add10(5)
// => 15
subtract20(5)
// => -15
Hello, Agent Double-Null0111.
Your mission, should you choose to accept it, is to write a pair of tools to help you infiltrate UMBRA (United Minions Being Really Awful) headquarters and retrieve the plans for their latest really awful scheme. There are two tools you will need to carry out this mission.
Once you have the secret plans, you will need to protect it so that only those who know the password can recover them.
In order to do this, you will need to implement the function
protectSecret(_ secret: String, withPassword password: String) -> (String) -> String
. This function will return another function that takes possible password strings as input. If the entered password is correct the new function returns the hidden secret, but if it is the incorrect password the function returns "Sorry. No hidden secrets here."
let secretFunction = protectSecret("Steal the world's supply of french fries!", withPassword: "5up3r53cr37")
secretFunction("Open sesame")
// => "Sorry. No hidden secrets here."
secretFunction("5up3r53cr37")
// => "Steal the world's supply of french fries!"
We have learned that UMBRA bases all of their safe combinations on the number of the room they are located in. The safe combinations are generated by repeatedly applying a special function to the room number. So the first number of the combination will come from applying the function to the room number, the second will come from applying the function to the first number, and the third from applying the function to the second number.
However, UMBRA regularly changes the function used to generate the combinations, so your tool will need to take both a room number and the appropriate function in order to generate the combination.
Implement the function
generateCombination(forRoom room: Int, usingFunction f: (Int) -> Int) -> (Int, Int, Int)
to generate the combination you will need.
func makeCombo(_ x: Int) -> Int {
(x * 13) % 256
}
generateCombination(forRoom: 227, usingFunction: makeCombo)
// => (135, 219, 31)
Sign up to Exercism to learn and master Swift with 35 concepts, 103 exercises, and real human mentoring, all for free.