Closures and Higher Order Functions
One of major key features in Swift is the closure. In Objective C, closures are known as blocks. The perception is similar, but they’re much more influential in Swift. Simply set, a closure is a function without a name. Swift closures are first class types, meaning that they can be allocate to variables just like any other type. Closures can also be passed into functions.
The closure is expressed as (parameters) -> returnType. For eg, a closure that captures a String and a Float as parameters and returns Void has the following type: (String, Float) -> Void. You can write down the functions that allow closures as parameters:
func myFunction(_ stringParameter: String, closureParameter: (String) -> Void) { closureParameter(stringParameter) }
The above mentioned function takes a string and a closure, and then it calls the closure, offering the string parameter to the closure. Here’s an example of using it:
myFunction("Hello, world!", closureParameter: {(string) in print(string) //prints "Hello, world!" })
In this case, we offer the closure to the function just like we offer a normal parameter. Then, in the process, we call the closure, just as we would call any other function declared with func. This example demonstrates that closures really are just unnamed functions. In other words, closures are referred to as anonymous functions, because that’s really what they are.
Sometimes Swift called as trailing closure syntax, this is syntax sugar for closures. This permits for fresher and more elegant closure expressions when a closure is the last parameter of a function. The following example, using the same function:
myFunction("Hello, world!") {(string) in print(string) //prints "Hello, world!" }
Trailing closure syntax allows us remove the parentheses around the closure parameter in a function, but only if it’s the last parameter. When writing functions, ensure you put the closure parameter last so that people who call your code can do so in a fresh and summarize manner.
Because closures in Swift are so influential, the Standard Library offers higher order functions. Set cleanly, a higher order function is a function that takes other function as a parameter. Higher order functions are present on collection types in Swift, providing functions such as map, filter, forEach, reduce, and flatMap. Let’s see one by one briefly:
Map
First, start with map. Type the following code in Playgrounds and observe what you find.
let mapNumbers = [1, 2, 3, 4, 5] let doubledMapNumbers = mapNumbers.map { $0 * 2 } print(doubledMapNumbers) //prints [2, 4, 6, 8, 10]
In the above mentioned code, it is an example of the map function. As you can observe, it is so easy to multiple each of the item in the mapNumbers array by 2 through the map function. You can perform that using for loop. The map function simply saves you a more code.
The map function obtains an input closure, which has one parameter of the similar type as the Element of the collection being mapped. This closure must also return a value of the similar type. In our case, the parameter is unspecified, meaning we do not explicitly provide a name for it. For this reason, we can refer to it as $0. Subsequent unspecified parameters can be referred to as $1, $2, $3, and so on.
On top, you may note that there is no return value of the closure. For one line closures, we don’t need to use the return keyword, as its oblique. Lastly, we utilize trailing closure syntax, which permits us to decrease the length of our code further. All of these syntactical shortcuts permit us to write really short, concise closure expressions. Here’s what the same expression let doubledMapNumbers = mapNumbers.map { $0 * 2 } would look like without these syntax improvements:
let doubledMapNumbers = mapNumbers.map( {(number) in return number * 2 })
As you can observe, Swift offers us with a serveral ways to shorten our closure expressions, permitting for clean code that’s simple to read.
Filter
Let’s look at higher order functions. State another array, filterNumbers
let filterNumbers = [1, 2, 3, 4, 5] let filteredNumbers = filterNumbers.filter { $0 > 3 } print(filteredNumbers) //prints [4, 5]
In this example, we filter filteredNumbers, maintaining only the items that are greater than 3. This example also makes use of Swift’s various syntax improvement to ensurethat our code is concise.
forEach
Now aim forEach, with an array called forEachNumbers:
let forEachNumbers = [1, 2, 3, 4, 5]
forEachNumbers.forEach { print($0) } //prints one item of the array on each line
By means of forEach is very similar to using a for loop. Yet again, we used many syntax enhancements to ensure we have clean code.
Reduce
Now, let’s confer reduce a try, with reduceNumbers:
let reduceNumbers = [1, 2, 3, 4 ,5] let reducedNumber = reduceNumbers.reduce(0) { $0 + $1 } print(reducedNumber) //prints 15
Reduce is used to decrease a collection down into a single value. In our case, we include all of the numbers together in reduceNumbers into one value, and store it in reducedNumber.
The parameter (i.e. 0) we offer for the reduce function is an initial value. Reduce begins with our initial value and execute the specified operation for every item in the collection.
0 + 1 + 2 + 3 + 4 + 5 is 15, which is why print(reducedNumbers) gives you 15
flatMap
Lastly, let’s use flatMap now. State an array called flatMapNumbers. This time, our initialing array is a small different than the previous example that it holds nil values:
let flatMapNumbers = [1, nil, 2, nil, 3, nil, 4, nil, 5] let flatMappedNumbers = flatMapNumbers.flatMap { $0 } print(flatMappedNumbers) //prints [1, 2, 3, 4, 5]
flatMap navigates via a collection, using the input closure to manipulate values. The resulting collection only contains non-nil values. You’ll observe that the resulting array holds no nil elements. flatMap is particularly helpful for eliminating optional values from a collection.
Chaining Them Together
I have told you flatmap will be the last example, but I have one more to explain you, and its really a combination of numerous transforms. You can chain higher order functions to make powerful transformations that aren’t possible with one transform. The following example:
let chainNumbers = [1, nil, 2, nil, 3, nil, 4, nil, 5]
let doubledNumbersOver8 = chainNumbers.flatMap { $0 }.filter { $0 > 3 }.map { $0 * 2 }
print(doubledNumbersOver8) //prints [8, 10]
I hope, these examples have demonstrated the power of closures and higher order functions in Swift. You can utilize these techniques in your apps to make clear your code and make it easier to maintain. Good luck!
Leave a Reply