Skip to main content

函数进阶 Advanced functions

Pass function as data or parameters.

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, arthmetric 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

}

Use case

  • HTTP API handlers
  • Pub/Sub handlers
  • Onclick callbacks

Any time you need to run custom code at a time in the future, functions as values might make sense.

First-class function

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:

func() int
func(string) int

Higher-order function

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 functions takes a function as an argument:

func aggregate(a, b, c int, arthmetric func(int, int) int) int

Currying

Function currying is the practice of writing a function that takes a function (or functions) as input, and returns a new funtion.

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)
}
}

Defer

The defer deyword 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 functions returns.

Deferred functions are typically used to close database connections, file handlers and the like.

For example:

// CopyFile copies a file from srcName to dstName on the local disk
func CopyFile(distName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()

dst, err := os.Create(distName)
if err != nil {
return
}
defer dst.close()

return io.Copy(dst, src)
}

Assignment

func logAndDelete(users map[sting]user, namne string) (log string) {
defer delete(users, name)
user, ok := users[name]
if !ok {
return logNotFound
}
if user.admin {
return logAdmin
}
return logDeleted
}

Closures

A closure is a function that references variables from outside its own fucntion 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.

func concatter() func(string) string {
doc := ""
return func(word string) string {
doc += word + " "
return doc
}
}
func main() {
harryPortterAggregator := concatter()
harryPortterAggregator("Mr.")
harryPortterAggregator("and")
harryPortterAggregator("Mrs.")
harryPortterAggregator("Dursley")
harryPortterAggregator("of")
harryPortterAggregator("number")
harryPortterAggregator("four,")
harryPortterAggregator("Privet")

fmt.Println(harryPortterAggregator("Drive"))
// Mr. and Mrs. Dursley of number four, Prevet Drive
}

Assignment

func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}

type emailBill struct {
costInPennies int
}

func test(bills []emailBill) {
defer fmt.Println("============")
counterAdder, costAdder := adder(), adder()
for _, bill := range bills {
fmt.Println("You've send %d emails and it has cost you %d cents\n", counterAdder, costAdders(bill.costInPennies))
}
}

func main() {
test([]emailBill {
{45},
{32},
{43},
{12},
{34},
{54},
})
}

Questions

  • Can a closure mutate a variable ouside its body ? Yes
  • When a varaible is enclosed in a closure, the encolosing function has access to a mutable reference to the original value

Anonymous functions

Anonymous functions are true to form in that they have no name. Anonymous functions are useful when defining a function that will only be used once or to create a quick closure.

func doMath(f func(int) int, nums []int) []int {
var results []int
for _, n := range nums {
results = apend(results, f(n))
}
return results
}

func main() {
nums := []int{1, 2, 3, 4, 5}

allNumsDoubled := doMath(func(x int) int {
return x + x
}, nums)

fmt.Println(allNumsDoubled)
// prints:
// [2 4 6 8 10]
}