W1349 Higher Order Functions
Prerequisites[edit]
![]() |
Coming Soon |
Lambda Expressions and Closures |
Lambda Expressions[edit]
Lambda expressions (also called "anonymous functions") are essentially the body of a function, i.e. the steps required to realize a specific algorithm, without being bound to an identifier. Lambda functions originate from the work of Alonzo Church in the 1930s.
Lambda expressions may contain multiple symbols, each of which must be bound to a value in order to completely evaluate the expression. Any symbol which has not yet been bound is termed free. In order to bind these free symbols we may rely on the language itself to provide meaning (such as for a literal constant), or we may rely on the surrounding context or environment. An open lambda expression is simply one in which some of the symbols are not yet bound. Such an expression can be closed by providing an environment which supplies definitions for all open symbols; a closure provides this necessary environment.
Hint: In Swift, lambda expressions are called "closures" even if they don't require such an environment.
Many of the functions which we've learned about and used so far are global functions. Essentially, global functions associate a name (the name of the function) and a block of code, a lambda expression.
The syntax for defining a named function is as follows:
func addTwo(_ n:Int) -> Int {
return n + 2
}
Note that the actual block of code is located within the braces.
We can create a lambda expression and assign it to a constant as follows:
let f = {(n:Int) -> Int in return n + 2}
This constant behaves much as we'd expect, and we can assign it to another constant if we'd like:
let g = f
g(4)
Swift provides us with several means of abbreviating the expression. If the type of the expression is known, we can infer the type from context:
let e : (Int) -> Int = {n in return n + 2}
e(4)
In Swift, we're able to take advantage of shorthand argument names. Each argument begins with a "$" and is numbered consecutively from zero:
let d : (Int) -> Int = {return $0 + 2}
d(5)
Swift also allows us to rely on implicit returns from single expression closures:
let c : (Int) -> Int = {$0 + 2}
c(7)
These shortcuts lead to very concise code. We can now pass these expressions to higher-order functions, i.e. functions which accept other functions as parameters. Let's look at a few examples:
1. Sorting
struct Student {
let name : String
let gpa : Float
}
let grades = [Student(name:"Nathan", gpa:4.5),
Student(name:"Enig", gpa:3.2),
Student(name:"Arthur", gpa:4.1)]
// Get an array of grades sorted by ascending GPA
let sortedGrades = grades.sorted() {$1.gpa > $0.gpa}
2. Mapping
let fruits = ["banana", "orange", "apple", "pomegranate"]
// Get an array containing the length of each string in fruits
let fruitNameLengths = fruits.map() {$0.count}
3. Filtering
struct Book {
let title : String
let author : String
let year : Int
}
let books = [Book(title:"Wuthering Heights", author:"Emily Bronte", year:1847),
Book(title:"Homage to Catalonia", author:"George Orwell", year:1938),
Book(title:"Notes from Underground", author:"Fyodor Dostoevsky", year:1864),
Book(title:"The Trial", author:"Franz Kafka", year:1925)]
// Get an array of books published before 1900
let booksPublishedBefore1900 = books.filter() {$0.year < 1900}
Exercises[edit]
Professor Snape needs your help to organize ingredients for a wide variety of potions. You'll need to verify blocks of code to ensure that they're doing exactly what Professor Snape needs. Be careful! Any mistakes can be hazardous to his health. In some cases, more than one answer will be correct.