Difference between revisions of "W1349 Higher Order Functions"
(*Added examples and fixed formatting*) |
m |
||
(One intermediate revision by the same user not shown) | |||
Line 4: | Line 4: | ||
{{ComingSoon|Lambda Expressions and Closures}} | {{ComingSoon|Lambda Expressions and Closures}} | ||
== | == Lambda Expressions == | ||
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 | 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. | 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 | 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 | 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 | 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''' | closed by providing an environment which supplies definitions for all open symbols; a '''closure''' | ||
Line 19: | Line 19: | ||
Many of the functions which we've learned about and used so far are global functions. Essentially, | 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 | 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: | The syntax for defining a named function is as follows: | ||
Line 31: | Line 31: | ||
Note that the actual block of code is located within the braces. | Note that the actual block of code is located within the braces. | ||
We can create a | We can create a lambda expression and assign it to a constant as follows: | ||
<syntaxhighlight lang="swift"> | <syntaxhighlight lang="swift"> | ||
Line 79: | Line 79: | ||
let grades = [Student(name:"Nathan", gpa:4.5), | let grades = [Student(name:"Nathan", gpa:4.5), | ||
Student(name:" | Student(name:"Enig", gpa:3.2), | ||
Student(name:" | Student(name:"Arthur", gpa:4.1)] | ||
// | // Get an array of grades sorted by ascending GPA | ||
let sortedGrades = grades.sorted() {$1.gpa > $0.gpa} | let sortedGrades = grades.sorted() {$1.gpa > $0.gpa} | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 91: | Line 91: | ||
let fruits = ["banana", "orange", "apple", "pomegranate"] | let fruits = ["banana", "orange", "apple", "pomegranate"] | ||
// | // Get an array containing the length of each string in fruits | ||
let fruitNameLengths = fruits.map() {$0.count} | let fruitNameLengths = fruits.map() {$0.count} | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 100: | Line 100: | ||
struct Book { | struct Book { | ||
let title : String | let title : String | ||
let author : String | |||
let year : Int | let year : Int | ||
} | } | ||
let books = [Book(title:"Wuthering Heights", year:1847), | let books = [Book(title:"Wuthering Heights", author:"Emily Bronte", year:1847), | ||
Book(title:"Homage to Catalonia", year:1938), | Book(title:"Homage to Catalonia", author:"George Orwell", year:1938), | ||
Book(title:"Notes from Underground", year:1864), | Book(title:"Notes from Underground", author:"Fyodor Dostoevsky", year:1864), | ||
Book(title:"The Trial", year:1925)] | Book(title:"The Trial", author:"Franz Kafka", year:1925)] | ||
// Get | // Get an array of books published before 1900 | ||
let | let booksPublishedBefore1900 = books.filter() {$0.year < 1900} | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Latest revision as of 12:09, 24 August 2020
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.