mirror of
https://github.com/bootdotdev/fcc-learn-golang-assets.git
synced 2025-12-13 16:51:17 +00:00
first
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func getFormattedMessages(messages []string, formatter func) []string {
|
||||
formattedMessages := []string{}
|
||||
for _, message := range messages {
|
||||
formattedMessages = append(formattedMessages, formatter(message))
|
||||
}
|
||||
return formattedMessages
|
||||
}
|
||||
|
||||
// don't touch below this line
|
||||
|
||||
func addSignature(message string) string {
|
||||
return message + " Kind regards."
|
||||
}
|
||||
|
||||
func addGreeting(message string) string {
|
||||
return "Hello! " + message
|
||||
}
|
||||
|
||||
func test(messages []string, formatter func(string) string) {
|
||||
defer fmt.Println("====================================")
|
||||
formattedMessages := getFormattedMessages(messages, formatter)
|
||||
if len(formattedMessages) != len(messages) {
|
||||
fmt.Println("The number of messages returned is incorrect.")
|
||||
return
|
||||
}
|
||||
for i, message := range messages {
|
||||
formatted := formattedMessages[i]
|
||||
fmt.Printf(" * %s -> %s\n", message, formatted)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
test([]string{
|
||||
"Thanks for getting back to me.",
|
||||
"Great to see you again.",
|
||||
"I would love to hang out this weekend.",
|
||||
"Got any hot stock tips?",
|
||||
}, addSignature)
|
||||
test([]string{
|
||||
"Thanks for getting back to me.",
|
||||
"Great to see you again.",
|
||||
"I would love to hang out this weekend.",
|
||||
"Got any hot stock tips?",
|
||||
}, addGreeting)
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func getFormattedMessages(messages []string, formatter func(string) string) []string {
|
||||
formattedMessages := []string{}
|
||||
for _, message := range messages {
|
||||
formattedMessages = append(formattedMessages, formatter(message))
|
||||
}
|
||||
return formattedMessages
|
||||
}
|
||||
|
||||
// don't touch below this line
|
||||
|
||||
func addSignature(message string) string {
|
||||
return message + " Kind regards."
|
||||
}
|
||||
|
||||
func addGreeting(message string) string {
|
||||
return "Hello! " + message
|
||||
}
|
||||
|
||||
func test(messages []string, formatter func(string) string) {
|
||||
defer fmt.Println("====================================")
|
||||
formattedMessages := getFormattedMessages(messages, formatter)
|
||||
if len(formattedMessages) != len(messages) {
|
||||
fmt.Println("The number of messages returned is incorrect.")
|
||||
return
|
||||
}
|
||||
for i, message := range messages {
|
||||
formatted := formattedMessages[i]
|
||||
fmt.Printf(" * %s -> %s\n", message, formatted)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
test([]string{
|
||||
"Thanks for getting back to me.",
|
||||
"Great to see you again.",
|
||||
"I would love to hang out this weekend.",
|
||||
"Got any hot stock tips?",
|
||||
}, addSignature)
|
||||
test([]string{
|
||||
"Thanks for getting back to me.",
|
||||
"Great to see you again.",
|
||||
"I would love to hang out this weekend.",
|
||||
"Got any hot stock tips?",
|
||||
}, addGreeting)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
* Thanks for getting back to me. -> Thanks for getting back to me. Kind regards.
|
||||
* Great to see you again. -> Great to see you again. Kind regards.
|
||||
* I would love to hang out this weekend. -> I would love to hang out this weekend. Kind regards.
|
||||
* Got any hot stock tips? -> Got any hot stock tips? Kind regards.
|
||||
====================================
|
||||
* Thanks for getting back to me. -> Hello! Thanks for getting back to me.
|
||||
* Great to see you again. -> Hello! Great to see you again.
|
||||
* I would love to hang out this weekend. -> Hello! I would love to hang out this weekend.
|
||||
* Got any hot stock tips? -> Hello! Got any hot stock tips?
|
||||
====================================
|
||||
@@ -0,0 +1,37 @@
|
||||
# First Class and Higher Order Functions
|
||||
|
||||
A programming language is said to have "first-class functions" when functions in that language are treated like any other variable. For example, in such a language, a function can be passed as an argument to other functions, can be returned by another function and can be assigned as a value to a variable.
|
||||
|
||||
A function that returns a function or accepts a function as input is called a Higher-Order Function.
|
||||
|
||||
Go supports [first-class](https://developer.mozilla.org/en-US/docs/Glossary/First-class_Function) and higher-order functions. Another way to think of this is that a function is just another type -- just like `int`s and `string`s and `bool`s.
|
||||
|
||||
For example, to accept a function as a parameter:
|
||||
|
||||
```go
|
||||
func add(x, y int) int {
|
||||
return x + y
|
||||
}
|
||||
|
||||
func mul(x, y int) int {
|
||||
return x * y
|
||||
}
|
||||
|
||||
// aggregate applies the given math function to the first 3 inputs
|
||||
func aggregate(a, b, c int, arithmetic func(int, int) int) int {
|
||||
return arithmetic(arithmetic(a, b), c)
|
||||
}
|
||||
|
||||
func main(){
|
||||
fmt.Println(aggregate(2,3,4, add))
|
||||
// prints 9
|
||||
fmt.Println(aggregate(2,3,4, mul))
|
||||
// prints 24
|
||||
}
|
||||
```
|
||||
|
||||
## Assignment
|
||||
|
||||
Textio is launching a new email messaging product, "Mailio"!
|
||||
|
||||
Fix the compile-time bug in the `getFormattedMessages` function. The function body is correct, but the function signature is not.
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"question": "What is a higher-order function?",
|
||||
"answers": [
|
||||
"A function that takes another function as an argument",
|
||||
"A function with superior logic",
|
||||
"A function that is first in the call stack"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# Why First-class and Higher-Order Functions?
|
||||
|
||||
At first, it may seem like dynamically creating functions and passing them around as variables adds unnecessary complexity. Most of the time you would be right. There are cases however when functions as values make a lot of sense. Some of these include:
|
||||
|
||||
* [HTTP API](https://en.wikipedia.org/wiki/Web_API) handlers
|
||||
* [Pub/Sub](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) handlers
|
||||
* Onclick callbacks
|
||||
|
||||
Any time you need to run custom code at *a time in the future*, functions as values might make sense.
|
||||
|
||||
## Definition: First-class Functions
|
||||
|
||||
A first-class function is a function that can be treated like any other value. Go supports first-class functions. A function's type is dependent on the types of its parameters and return values. For example, these are different function types:
|
||||
|
||||
```go
|
||||
func() int
|
||||
```
|
||||
|
||||
```go
|
||||
func(string) int
|
||||
```
|
||||
|
||||
## Definition: Higher-Order Functions
|
||||
|
||||
A higher-order function is a function that takes a function as an argument or returns a function as a return value. Go supports higher-order functions. For example, this function takes a function as an argument:
|
||||
|
||||
```go
|
||||
func aggregate(a, b, c int, arithmetic func(int, int) int) int
|
||||
```
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"question": "What is a first-class function?",
|
||||
"answers": [
|
||||
"A function that is treated like any other variable",
|
||||
"A function that has been deemed most important by the architect",
|
||||
"A function that takes another function as an argument"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# Why First-class and Higher-Order Functions?
|
||||
|
||||
At first, it may seem like dynamically creating functions and passing them around as variables adds unnecessary complexity. Most of the time you would be right. There are cases however when functions as values make a lot of sense. Some of these include:
|
||||
|
||||
* [HTTP API](https://en.wikipedia.org/wiki/Web_API) handlers
|
||||
* [Pub/Sub](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) handlers
|
||||
* Onclick callbacks
|
||||
|
||||
Any time you need to run custom code at *a time in the future*, functions as values might make sense.
|
||||
|
||||
## Definition: First-class Functions
|
||||
|
||||
A first-class function is a function that can be treated like any other value. Go supports first-class functions. A function's type is dependent on the types of its parameters and return values. For example, these are different function types:
|
||||
|
||||
```go
|
||||
func() int
|
||||
```
|
||||
|
||||
```go
|
||||
func(string) int
|
||||
```
|
||||
|
||||
## Definition: Higher-Order Functions
|
||||
|
||||
A higher-order function is a function that takes a function as an argument or returns a function as a return value. Go supports higher-order functions. For example, this function takes a function as an argument:
|
||||
|
||||
```go
|
||||
func aggregate(a, b, c int, arithmetic func(int, int) int) int
|
||||
```
|
||||
47
course/10-advanced_functions/exercises/3-currying/code.go
Normal file
47
course/10-advanced_functions/exercises/3-currying/code.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// getLogger takes a function that formats two strings into
|
||||
// a single string and returns a function that formats two strings but prints
|
||||
// the result instead of returning it
|
||||
func getLogger(formatter func(string, string) string) func(string, string) {
|
||||
// ?
|
||||
}
|
||||
|
||||
// don't touch below this line
|
||||
|
||||
func test(first string, errors []error, formatter func(string, string) string) {
|
||||
defer fmt.Println("====================================")
|
||||
logger := getLogger(formatter)
|
||||
fmt.Println("Logs:")
|
||||
for _, err := range errors {
|
||||
logger(first, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func colonDelimit(first, second string) string {
|
||||
return first + ": " + second
|
||||
}
|
||||
func commaDelimit(first, second string) string {
|
||||
return first + ", " + second
|
||||
}
|
||||
|
||||
func main() {
|
||||
dbErrors := []error{
|
||||
errors.New("out of memory"),
|
||||
errors.New("cpu is pegged"),
|
||||
errors.New("networking issue"),
|
||||
errors.New("invalid syntax"),
|
||||
}
|
||||
test("Error on database server", dbErrors, colonDelimit)
|
||||
|
||||
mailErrors := []error{
|
||||
errors.New("email too large"),
|
||||
errors.New("non alphanumeric symbols found"),
|
||||
}
|
||||
test("Error on mail server", mailErrors, commaDelimit)
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// getLogger takes a function that formats two strings into
|
||||
// a single string and returns a function that formats two strings but prints
|
||||
// the result instead of returning it
|
||||
func getLogger(formatter func(string, string) string) func(string, string) {
|
||||
return func(first, second string) {
|
||||
fmt.Println(formatter(first, second))
|
||||
}
|
||||
}
|
||||
|
||||
// don't touch below this line
|
||||
|
||||
func test(first string, errors []error, formatter func(string, string) string) {
|
||||
defer fmt.Println("====================================")
|
||||
logger := getLogger(formatter)
|
||||
fmt.Println("Logs:")
|
||||
for _, err := range errors {
|
||||
logger(first, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func colonDelimit(first, second string) string {
|
||||
return first + ": " + second
|
||||
}
|
||||
func commaDelimit(first, second string) string {
|
||||
return first + ", " + second
|
||||
}
|
||||
|
||||
func main() {
|
||||
dbErrors := []error{
|
||||
errors.New("out of memory"),
|
||||
errors.New("cpu is pegged"),
|
||||
errors.New("networking issue"),
|
||||
errors.New("invalid syntax"),
|
||||
}
|
||||
test("Error on database server", dbErrors, colonDelimit)
|
||||
|
||||
mailErrors := []error{
|
||||
errors.New("email too large"),
|
||||
errors.New("non alphanumeric symbols found"),
|
||||
}
|
||||
test("Error on mail server", mailErrors, commaDelimit)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
Logs:
|
||||
Error on database server: out of memory
|
||||
Error on database server: cpu is pegged
|
||||
Error on database server: networking issue
|
||||
Error on database server: invalid syntax
|
||||
====================================
|
||||
Logs:
|
||||
Error on mail server, email too large
|
||||
Error on mail server, non alphanumeric symbols found
|
||||
====================================
|
||||
40
course/10-advanced_functions/exercises/3-currying/readme.md
Normal file
40
course/10-advanced_functions/exercises/3-currying/readme.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Currying
|
||||
|
||||
Function currying is the practice of writing a function that takes a function (or functions) as input, and returns a new function.
|
||||
|
||||
For example:
|
||||
|
||||
```go
|
||||
func main() {
|
||||
squareFunc := selfMath(multiply)
|
||||
doubleFunc := selfMath(add)
|
||||
|
||||
fmt.Println(squareFunc(5))
|
||||
// prints 25
|
||||
|
||||
fmt.Println(doubleFunc(5))
|
||||
// prints 10
|
||||
}
|
||||
|
||||
func multiply(x, y int) int {
|
||||
return x * y
|
||||
}
|
||||
|
||||
func add(x, y int) int {
|
||||
return x + y
|
||||
}
|
||||
|
||||
func selfMath(mathFunc func(int, int) int) func (int) int {
|
||||
return func(x int) int {
|
||||
return mathFunc(x, x)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In the example above, the `selfMath` function takes in a function as its parameter, and returns a function that itself returns the value of running that input function on its parameter.
|
||||
|
||||
## Assignment
|
||||
|
||||
The Mailio API needs a very robust error-logging system so we can see when things are going awry in the back-end system. We need a function that can create a custom "logger" (a function that prints to the console) given a specific formatter.
|
||||
|
||||
Complete the `getLogger` function. It should `return` *a new function* that prints the formatted inputs using the given `formatter` function. The inputs should be passed into the formatter function in the order they are given to the logger function.
|
||||
91
course/10-advanced_functions/exercises/4-defer/code.go
Normal file
91
course/10-advanced_functions/exercises/4-defer/code.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
const (
|
||||
logDeleted = "user deleted"
|
||||
logNotFound = "user not found"
|
||||
logAdmin = "admin deleted"
|
||||
)
|
||||
|
||||
func logAndDelete(users map[string]user, name string) (log string) {
|
||||
user, ok := users[name]
|
||||
if !ok {
|
||||
delete(users, name)
|
||||
return logNotFound
|
||||
}
|
||||
if user.admin {
|
||||
return logAdmin
|
||||
}
|
||||
delete(users, name)
|
||||
return logDeleted
|
||||
}
|
||||
|
||||
// don't touch below this line
|
||||
|
||||
type user struct {
|
||||
name string
|
||||
number int
|
||||
admin bool
|
||||
}
|
||||
|
||||
func test(users map[string]user, name string) {
|
||||
fmt.Printf("Attempting to delete %s...\n", name)
|
||||
defer fmt.Println("====================================")
|
||||
log := logAndDelete(users, name)
|
||||
fmt.Println("Log:", log)
|
||||
}
|
||||
|
||||
func main() {
|
||||
users := map[string]user{
|
||||
"john": {
|
||||
name: "john",
|
||||
number: 18965554631,
|
||||
admin: true,
|
||||
},
|
||||
"elon": {
|
||||
name: "elon",
|
||||
number: 19875556452,
|
||||
admin: true,
|
||||
},
|
||||
"breanna": {
|
||||
name: "breanna",
|
||||
number: 98575554231,
|
||||
admin: false,
|
||||
},
|
||||
"kade": {
|
||||
name: "kade",
|
||||
number: 10765557221,
|
||||
admin: false,
|
||||
},
|
||||
}
|
||||
|
||||
fmt.Println("Initial users:")
|
||||
usersSorted := []string{}
|
||||
for name := range users {
|
||||
usersSorted = append(usersSorted, name)
|
||||
}
|
||||
sort.Strings(usersSorted)
|
||||
for _, name := range usersSorted {
|
||||
fmt.Println(" -", name)
|
||||
}
|
||||
fmt.Println("====================================")
|
||||
|
||||
test(users, "john")
|
||||
test(users, "santa")
|
||||
test(users, "kade")
|
||||
|
||||
fmt.Println("Final users:")
|
||||
usersSorted = []string{}
|
||||
for name := range users {
|
||||
usersSorted = append(usersSorted, name)
|
||||
}
|
||||
sort.Strings(usersSorted)
|
||||
for _, name := range usersSorted {
|
||||
fmt.Println(" -", name)
|
||||
}
|
||||
fmt.Println("====================================")
|
||||
}
|
||||
91
course/10-advanced_functions/exercises/4-defer/complete.go
Normal file
91
course/10-advanced_functions/exercises/4-defer/complete.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
const (
|
||||
logDeleted = "user deleted"
|
||||
logNotFound = "user not found"
|
||||
logAdmin = "admin deleted"
|
||||
)
|
||||
|
||||
func logAndDelete(users map[string]user, name string) (log string) {
|
||||
defer delete(users, name)
|
||||
|
||||
user, ok := users[name]
|
||||
if !ok {
|
||||
return logNotFound
|
||||
}
|
||||
if user.admin {
|
||||
return logAdmin
|
||||
}
|
||||
return logDeleted
|
||||
}
|
||||
|
||||
// don't touch below this line
|
||||
|
||||
type user struct {
|
||||
name string
|
||||
number int
|
||||
admin bool
|
||||
}
|
||||
|
||||
func test(users map[string]user, name string) {
|
||||
fmt.Printf("Attempting to delete %s...\n", name)
|
||||
defer fmt.Println("====================================")
|
||||
log := logAndDelete(users, name)
|
||||
fmt.Println("Log:", log)
|
||||
}
|
||||
|
||||
func main() {
|
||||
users := map[string]user{
|
||||
"john": {
|
||||
name: "john",
|
||||
number: 18965554631,
|
||||
admin: true,
|
||||
},
|
||||
"elon": {
|
||||
name: "elon",
|
||||
number: 19875556452,
|
||||
admin: true,
|
||||
},
|
||||
"breanna": {
|
||||
name: "breanna",
|
||||
number: 98575554231,
|
||||
admin: false,
|
||||
},
|
||||
"kade": {
|
||||
name: "kade",
|
||||
number: 10765557221,
|
||||
admin: false,
|
||||
},
|
||||
}
|
||||
|
||||
fmt.Println("Initial users:")
|
||||
usersSorted := []string{}
|
||||
for name := range users {
|
||||
usersSorted = append(usersSorted, name)
|
||||
}
|
||||
sort.Strings(usersSorted)
|
||||
for _, name := range usersSorted {
|
||||
fmt.Println(" -", name)
|
||||
}
|
||||
fmt.Println("====================================")
|
||||
|
||||
test(users, "john")
|
||||
test(users, "santa")
|
||||
test(users, "kade")
|
||||
|
||||
fmt.Println("Final users:")
|
||||
usersSorted = []string{}
|
||||
for name := range users {
|
||||
usersSorted = append(usersSorted, name)
|
||||
}
|
||||
sort.Strings(usersSorted)
|
||||
for _, name := range usersSorted {
|
||||
fmt.Println(" -", name)
|
||||
}
|
||||
fmt.Println("====================================")
|
||||
}
|
||||
19
course/10-advanced_functions/exercises/4-defer/expected.txt
Normal file
19
course/10-advanced_functions/exercises/4-defer/expected.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
Initial users:
|
||||
- breanna
|
||||
- elon
|
||||
- john
|
||||
- kade
|
||||
====================================
|
||||
Attempting to delete john...
|
||||
Log: admin deleted
|
||||
====================================
|
||||
Attempting to delete santa...
|
||||
Log: user not found
|
||||
====================================
|
||||
Attempting to delete kade...
|
||||
Log: user deleted
|
||||
====================================
|
||||
Final users:
|
||||
- breanna
|
||||
- elon
|
||||
====================================
|
||||
46
course/10-advanced_functions/exercises/4-defer/readme.md
Normal file
46
course/10-advanced_functions/exercises/4-defer/readme.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Defer
|
||||
|
||||
The `defer` keyword is a fairly unique feature of Go. It allows a function to be executed automatically *just before* its enclosing function returns.
|
||||
|
||||
The deferred call's arguments are evaluated immediately, but the function call is not executed until the surrounding function returns.
|
||||
|
||||
Deferred functions are typically used to close database connections, file handlers and the like.
|
||||
|
||||
For example:
|
||||
|
||||
```go
|
||||
// CopyFile copies a file from srcName to dstName on the local filesystem.
|
||||
func CopyFile(dstName, srcName string) (written int64, err error) {
|
||||
|
||||
// Open the source file
|
||||
src, err := os.Open(srcName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Close the source file when the CopyFile function returns
|
||||
defer src.Close()
|
||||
|
||||
// Create the destination file
|
||||
dst, err := os.Create(dstName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Close the destination file when the CopyFile function returns
|
||||
defer dst.Close()
|
||||
|
||||
return io.Copy(dst, src)
|
||||
}
|
||||
```
|
||||
|
||||
In the above example, the `src.Close()` function is not called until after the `CopyFile` function was called but immediately before the `CopyFile` function returns.
|
||||
|
||||
Defer is a great way to **make sure** that something happens at the end of a function, even if there are multiple return statements.
|
||||
|
||||
## Assignment
|
||||
|
||||
There is a bug in the `logAndDelete` function, fix it!
|
||||
|
||||
This function should *always* delete the user from the user's map, which is a map that stores the user's name as keys. It also returns a `log` string that indicates to the caller some information about the user's deletion.
|
||||
|
||||
To avoid bugs like this in the future, instead of calling `delete` before each `return`, just `defer` the delete once at the beginning of the function.
|
||||
|
||||
46
course/10-advanced_functions/exercises/5-closures/code.go
Normal file
46
course/10-advanced_functions/exercises/5-closures/code.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func adder() func(int) int {
|
||||
// ?
|
||||
}
|
||||
|
||||
// don't touch below this line
|
||||
|
||||
type emailBill struct {
|
||||
costInPennies int
|
||||
}
|
||||
|
||||
func test(bills []emailBill) {
|
||||
defer fmt.Println("====================================")
|
||||
countAdder, costAdder := adder(), adder()
|
||||
for _, bill := range bills {
|
||||
fmt.Printf("You've sent %d emails and it has cost you %d cents\n", countAdder(1), costAdder(bill.costInPennies))
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
test([]emailBill{
|
||||
{45},
|
||||
{32},
|
||||
{43},
|
||||
{12},
|
||||
{34},
|
||||
{54},
|
||||
})
|
||||
|
||||
test([]emailBill{
|
||||
{12},
|
||||
{12},
|
||||
{976},
|
||||
{12},
|
||||
{543},
|
||||
})
|
||||
|
||||
test([]emailBill{
|
||||
{743},
|
||||
{13},
|
||||
{8},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func adder() func(int) int {
|
||||
sum := 0
|
||||
return func(x int) int {
|
||||
sum += x
|
||||
return sum
|
||||
}
|
||||
}
|
||||
|
||||
// don't touch below this line
|
||||
|
||||
type emailBill struct {
|
||||
costInPennies int
|
||||
}
|
||||
|
||||
func test(bills []emailBill) {
|
||||
defer fmt.Println("====================================")
|
||||
countAdder, costAdder := adder(), adder()
|
||||
for _, bill := range bills {
|
||||
fmt.Printf("You've sent %d emails and it has cost you %d cents\n", countAdder(1), costAdder(bill.costInPennies))
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
test([]emailBill{
|
||||
{45},
|
||||
{32},
|
||||
{43},
|
||||
{12},
|
||||
{34},
|
||||
{54},
|
||||
})
|
||||
|
||||
test([]emailBill{
|
||||
{12},
|
||||
{12},
|
||||
{976},
|
||||
{12},
|
||||
{543},
|
||||
})
|
||||
|
||||
test([]emailBill{
|
||||
{743},
|
||||
{13},
|
||||
{8},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
You've sent 1 emails and it has cost you 45 cents
|
||||
You've sent 2 emails and it has cost you 77 cents
|
||||
You've sent 3 emails and it has cost you 120 cents
|
||||
You've sent 4 emails and it has cost you 132 cents
|
||||
You've sent 5 emails and it has cost you 166 cents
|
||||
You've sent 6 emails and it has cost you 220 cents
|
||||
====================================
|
||||
You've sent 1 emails and it has cost you 12 cents
|
||||
You've sent 2 emails and it has cost you 24 cents
|
||||
You've sent 3 emails and it has cost you 1000 cents
|
||||
You've sent 4 emails and it has cost you 1012 cents
|
||||
You've sent 5 emails and it has cost you 1555 cents
|
||||
====================================
|
||||
You've sent 1 emails and it has cost you 743 cents
|
||||
You've sent 2 emails and it has cost you 756 cents
|
||||
You've sent 3 emails and it has cost you 764 cents
|
||||
====================================
|
||||
37
course/10-advanced_functions/exercises/5-closures/readme.md
Normal file
37
course/10-advanced_functions/exercises/5-closures/readme.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Closures
|
||||
|
||||
A closure is a function that references variables from outside its own function body. The function may access and *assign* to the referenced variables.
|
||||
|
||||
In this example, the `concatter()` function returns a function that has reference to an *enclosed* `doc` value. Each successive call to `harryPotterAggregator` mutates that same `doc` variable.
|
||||
|
||||
```go
|
||||
func concatter() func(string) string {
|
||||
doc := ""
|
||||
return func(word string) string {
|
||||
doc += word + " "
|
||||
return doc
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
harryPotterAggregator := concatter()
|
||||
harryPotterAggregator("Mr.")
|
||||
harryPotterAggregator("and")
|
||||
harryPotterAggregator("Mrs.")
|
||||
harryPotterAggregator("Dursley")
|
||||
harryPotterAggregator("of")
|
||||
harryPotterAggregator("number")
|
||||
harryPotterAggregator("four,")
|
||||
harryPotterAggregator("Privet")
|
||||
|
||||
fmt.Println(harryPotterAggregator("Drive"))
|
||||
// Mr. and Mrs. Dursley of number four, Privet Drive
|
||||
}
|
||||
```
|
||||
|
||||
## Assignment
|
||||
|
||||
Keeping track of how many emails we send is mission-critical at Mailio. Complete the `adder()` function.
|
||||
|
||||
It should return a function that adds its input (an `int`) to an enclosed `sum` value, then return the new sum. In other words, it keeps a running total of the `sum` variable within a closure.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"question": "Can a closure mutate a variable outside its body?",
|
||||
"answers": [
|
||||
"Yes",
|
||||
"No"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
# Closure Review
|
||||
|
||||
A closure is a function that references variables from outside its own function body. The function may access and *assign* to the referenced variables.
|
||||
|
||||
## Example Closure
|
||||
|
||||
```go
|
||||
func concatter() func(string) string {
|
||||
doc := ""
|
||||
return func(word string) string {
|
||||
doc += word + " "
|
||||
return doc
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
harryPotterAggregator := concatter()
|
||||
harryPotterAggregator("Mr.")
|
||||
harryPotterAggregator("and")
|
||||
harryPotterAggregator("Mrs.")
|
||||
harryPotterAggregator("Dursley")
|
||||
harryPotterAggregator("of")
|
||||
harryPotterAggregator("number")
|
||||
harryPotterAggregator("four,")
|
||||
harryPotterAggregator("Privet")
|
||||
|
||||
fmt.Println(harryPotterAggregator("Drive"))
|
||||
// Mr. and Mrs. Dursley of number four, Privet Drive
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"question": "When a variable is enclosed in a closure, the enclosing function has access to ____",
|
||||
"answers": [
|
||||
"a mutable reference to the original value",
|
||||
"a copy of the value"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
# Closure Review
|
||||
|
||||
A closure is a function that references variables from outside its own function body. The function may access and *assign* to the referenced variables.
|
||||
|
||||
## Example Closure
|
||||
|
||||
```go
|
||||
func concatter() func(string) string {
|
||||
doc := ""
|
||||
return func(word string) string {
|
||||
doc += word + " "
|
||||
return doc
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
harryPotterAggregator := concatter()
|
||||
harryPotterAggregator("Mr.")
|
||||
harryPotterAggregator("and")
|
||||
harryPotterAggregator("Mrs.")
|
||||
harryPotterAggregator("Dursley")
|
||||
harryPotterAggregator("of")
|
||||
harryPotterAggregator("number")
|
||||
harryPotterAggregator("four,")
|
||||
harryPotterAggregator("Privet")
|
||||
|
||||
fmt.Println(harryPotterAggregator("Drive"))
|
||||
// Mr. and Mrs. Dursley of number four, Privet Drive
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func printReports(messages []string) {
|
||||
// ?
|
||||
}
|
||||
|
||||
// don't touch below this line
|
||||
|
||||
func test(messages []string) {
|
||||
defer fmt.Println("====================================")
|
||||
printReports(messages)
|
||||
}
|
||||
|
||||
func main() {
|
||||
test([]string{
|
||||
"Here's Johnny!",
|
||||
"Go ahead, make my day",
|
||||
"You had me at hello",
|
||||
"There's no place like home",
|
||||
})
|
||||
|
||||
test([]string{
|
||||
"Hello, my name is Inigo Montoya. You killed my father. Prepare to die.",
|
||||
"May the Force be with you.",
|
||||
"Show me the money!",
|
||||
"Go ahead, make my day.",
|
||||
})
|
||||
}
|
||||
|
||||
func printCostReport(costCalculator func(string) int, message string) {
|
||||
cost := costCalculator(message)
|
||||
fmt.Printf(`Message: "%s" Cost: %v cents`, message, cost)
|
||||
fmt.Println()
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func printReports(messages []string) {
|
||||
for _, message := range messages {
|
||||
printCostReport(func(m string) int {
|
||||
return len(m) * 2
|
||||
}, message)
|
||||
}
|
||||
}
|
||||
|
||||
// don't touch below this line
|
||||
|
||||
func test(messages []string) {
|
||||
defer fmt.Println("====================================")
|
||||
printReports(messages)
|
||||
}
|
||||
|
||||
func main() {
|
||||
test([]string{
|
||||
"Here's Johnny!",
|
||||
"Go ahead, make my day",
|
||||
"You had me at hello",
|
||||
"There's no place like home",
|
||||
})
|
||||
|
||||
test([]string{
|
||||
"Hello, my name is Inigo Montoya. You killed my father. Prepare to die.",
|
||||
"May the Force be with you.",
|
||||
"Show me the money!",
|
||||
"Go ahead, make my day.",
|
||||
})
|
||||
}
|
||||
|
||||
func printCostReport(costCalculator func(string) int, message string) {
|
||||
cost := costCalculator(message)
|
||||
fmt.Printf(`Message: "%s" Cost: %v cents`, message, cost)
|
||||
fmt.Println()
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
Message: "Here's Johnny!" Cost: 28 cents
|
||||
Message: "Go ahead, make my day" Cost: 42 cents
|
||||
Message: "You had me at hello" Cost: 38 cents
|
||||
Message: "There's no place like home" Cost: 52 cents
|
||||
====================================
|
||||
Message: "Hello, my name is Inigo Montoya. You killed my father. Prepare to die." Cost: 140 cents
|
||||
Message: "May the Force be with you." Cost: 52 cents
|
||||
Message: "Show me the money!" Cost: 36 cents
|
||||
Message: "Go ahead, make my day." Cost: 44 cents
|
||||
====================================
|
||||
@@ -0,0 +1,38 @@
|
||||
# Anonymous Functions
|
||||
|
||||
Anonymous functions are true to form in that they have *no name*. We've been using them throughout this chapter, but we haven't really talked about them yet.
|
||||
|
||||
Anonymous functions are useful when defining a function that will only be used once or to create a quick [closure](https://en.wikipedia.org/wiki/Closure_(computer_programming)).
|
||||
|
||||
```go
|
||||
// doMath accepts a function that converts one int into another
|
||||
// and a slice of ints. It returns a slice of ints that have been
|
||||
// converted by the passed in function.
|
||||
func doMath(f func(int) int, nums []int) []int {
|
||||
var results []int
|
||||
for _, n := range nums {
|
||||
results = append(results, f(n))
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
|
||||
// Here we define an anonymous function that doubles an int
|
||||
// and pass it to doMath
|
||||
allNumsDoubled := doMath(func(x int) int {
|
||||
return x + x
|
||||
}, nums)
|
||||
|
||||
fmt.Println(allNumsDoubled)
|
||||
// prints:
|
||||
// [2 4 6 8 10]
|
||||
}
|
||||
```
|
||||
|
||||
## Assignment
|
||||
|
||||
Complete the `printReports` function.
|
||||
|
||||
Call `printCostReport` once for each message. Pass in an anonymous function as the `costCalculator` that returns an `int` equal to twice the length of the input message.
|
||||
Reference in New Issue
Block a user