Difference between revisions of "W1349 Higher Order Functions"
Tag: Rollback |
(*Added examples and fixed formatting*) |
||
Line 6: | Line 6: | ||
== Lamda Expressions == | == Lamda Expressions == | ||
Lamda expressions (also called "anonymous functions") are essentially the body of a function, i.e. | Lamda expressions (also called "anonymous functions") are essentially the body of a function, i.e. | ||
the steps | 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 1930's. | ||
functions originate from the work of Alonzo Church in the 1930's. | |||
Lamda expressions may contain | Lamda expressions may contain multiple symbols, each of which must be bound to a value in order | ||
to completely evaluate the expression. | 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. | 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. | expression is simply one in which some of the symbols are not yet bound. Such an expression can be | ||
closed by providing an | closed by providing an environment which supplies definitions for all open symbols; a '''closure''' | ||
provides this necessary environment. | provides this necessary environment. | ||
Line 20: | 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 lamda expression. | |||
The syntax for | The syntax for defining a named function is as follows: | ||
<syntaxhighlight lang="swift"> | |||
func addTwo(_ n:Int) -> Int { | func addTwo(_ n:Int) -> Int { | ||
return n + 2 | return n + 2 | ||
} | } | ||
</syntaxhighlight> | |||
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 lamda expression as follows: | We can create a lamda expression and assign it to a constant as follows: | ||
<syntaxhighlight lang="swift"> | |||
let f = {(n:Int) -> Int in return n + 2} | let f = {(n:Int) -> Int in return n + 2} | ||
</syntaxhighlight> | |||
This constant behaves much as we'd expect, and we can assign it to another constant if we'd like: | This constant behaves much as we'd expect, and we can assign it to another constant if we'd like: | ||
f( | <syntaxhighlight lang="swift"> | ||
let g = f | |||
g(4) | |||
</syntaxhighlight> | |||
Swift provides us with several means of abbreviating the expression. | Swift provides us with several means of abbreviating the expression. If the type of the expression | ||
is known, we can | is known, we can '''infer the type from context''': | ||
<syntaxhighlight lang="swift"> | |||
let e : (Int) -> Int = {n in return n + 2} | let e : (Int) -> Int = {n in return n + 2} | ||
e(4) | e(4) | ||
</syntaxhighlight> | |||
In Swift, we're able to take advantage of | In Swift, we're able to take advantage of '''shorthand argument names'''. Each argument begins with a "$" and | ||
is numbered consecutively from zero: | is numbered consecutively from zero: | ||
<syntaxhighlight lang="swift"> | |||
let d : (Int) -> Int = {return $0 + 2} | let d : (Int) -> Int = {return $0 + 2} | ||
d(5) | d(5) | ||
</syntaxhighlight> | |||
Swift also allows us to rely on | Swift also allows us to rely on '''implicit returns''' from single expression closures: | ||
<syntaxhighlight lang="swift"> | |||
let c : (Int) -> Int = {$0 + 2} | let c : (Int) -> Int = {$0 + 2} | ||
c(7) | c(7) | ||
</syntaxhighlight> | |||
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 | |||
<syntaxhighlight lang="swift"> | |||
struct Student { | |||
let name : String | |||
let gpa : Float | |||
} | |||
let grades = [Student(name:"Nathan", gpa:4.5), | |||
Student(name:"Arthur", gpa:4.1), | |||
Student(name:"Enig", gpa:3.2)] | |||
// Sorted by ascending gpa | |||
let sortedGrades = grades.sorted() {$1.gpa > $0.gpa} | |||
</syntaxhighlight> | |||
2. Mapping | 2. Mapping | ||
<syntaxhighlight lang="swift"> | |||
let fruits = ["banana", "orange", "apple", "pomegranate"] | |||
// | // Replace every entry in fruits with the length of each fruit's name | ||
let fruitNameLengths = fruits.map() {$0.count} | |||
</syntaxhighlight> | |||
3. Filtering | |||
<syntaxhighlight lang="swift"> | |||
struct Book { | |||
struct | let title : String | ||
let | let year : Int | ||
let | |||
} | } | ||
let books = [Book(title:"Wuthering Heights", year:1847), | |||
Book(title:"Homage to Catalonia", year:1938), | |||
Book(title:"Notes from Underground", year:1864), | |||
Book(title:"The Trial", year:1925)] | |||
let | // Get a list of books published before 1900 | ||
let olderBooks = books.filter() {$0.year < 1900} | |||
</syntaxhighlight> | |||
== Exercises == | == Exercises == | ||
Professor Snape needs your help to organize ingredients for a wide variety of potions. | 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.''' | ||
<quiz shuffleanswers=false display=simple> | <quiz shuffleanswers=false display=simple> |
Revision as of 23:27, 5 December 2019
Prerequisites[edit]
Coming Soon | |
Lambda Expressions and Closures |
Lamda Expressions[edit]
Lamda 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 1930's.
Lamda 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 lamda 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 lamda 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:"Arthur", gpa:4.1),
Student(name:"Enig", gpa:3.2)]
// Sorted by ascending gpa
let sortedGrades = grades.sorted() {$1.gpa > $0.gpa}
2. Mapping
let fruits = ["banana", "orange", "apple", "pomegranate"]
// Replace every entry in fruits with the length of each fruit's name
let fruitNameLengths = fruits.map() {$0.count}
3. Filtering
struct Book {
let title : String
let year : Int
}
let books = [Book(title:"Wuthering Heights", year:1847),
Book(title:"Homage to Catalonia", year:1938),
Book(title:"Notes from Underground", year:1864),
Book(title:"The Trial", year:1925)]
// Get a list of books published before 1900
let olderBooks = 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.