zoukankan      html  css  js  c++  java
  • go之闭包及其应用

    https://www.calhoun.io/5-useful-ways-to-use-closures-in-go/

    5种有用的闭包

    在本文中,我们将探索闭包和匿名函数的几种不同的实际用例,以便您能够更好地理解闭包何时是合适的,并了解它们如何应用于不同的情况。

    1.隔离数据

    假设您想创建一个函数,该函数可以访问即使在函数退出之后仍然存在的数据。例如,您想要计算函数被调用了多少次,或者您想要创建一个fibonacci数字生成器,但是您不想让任何人访问该数据(这样他们就不会意外地更改它)。您可以使用闭包来实现这一点。

     1 package main
     2 
     3 import "fmt"
     4 
     5 func main() {
     6   gen := makeFibGen()
     7   for i := 0; i < 10; i++ {
     8     fmt.Println(gen())
     9   }
    10 }
    11 
    12 func makeFibGen() func() int {
    13   f1 := 0
    14   f2 := 1
    15   return func() int {
    16     f2, f1 = (f1 + f2), f2
    17     return f1
    18   }
    19 }
    View Code

    当然,您可以使用自定义类型来创建非常类似的东西,但是如果您希望使用多个数字生成器,您可能最终需要声明一个接口,并将其作为使用生成器的其他函数的参数接受,比如这样。

    1 type Generator interface {
    2   Next() int
    3 }
    4 
    5 func doWork(g Generator) {
    6   n := g.Next()
    7   fmt.Println(n)
    8   // ... do work with n
    9 }
    View Code

    使用闭包,您可以只要求函数作为参数传入,因为无论如何,您实际上只关心生成器接口中的一个方法。

    func doWork(f func() int) {
      n := f()
      fmt.Println(n)
      // ... do work with n
    }

    2.封装函数并创建中间件

    函数是Go语言的一等公民,这意味着您不仅可以动态地创建匿名函数,还可以将函数作为参数传递给函数。例如,在创建web服务器时,通常提供一个函数来处理特定路由的web请求。

    package main
    
    import (
      "fmt"
      "net/http"
    )
    
    func main() {
      http.HandleFunc("/hello", hello)
      http.ListenAndServe(":3000", nil)
    }
    
    func hello(w http.ResponseWriter, r *http.Request) {
      fmt.Fprintln(w, "<h1>Hello!</h1>")
    }

    虽然这段代码不需要闭包,但是如果我们想用更多的逻辑来包装处理程序,闭包是非常有用的。一个完美的例子是,当我们想要在处理程序执行之前或之后创建中间件来执行工作时。

    什么是中间件:

    中间件基本上是可重用函数的一个花哨术语,可在设计用于处理web requst的代码之前和之后运行代码。在Go中,这些通常是用闭包来完成的,但是在不同的编程语言中,可以用其他方法来完成。 

    在编写web应用程序时,使用中间件是很常见的,它们不仅仅对计时器有用(您将在下面看到一个示例)。例如,可以使用中间件编写代码来验证用户是否登录过一次,然后将其应用于所有仅限成员的页面。

    package main
    
    import (
      "fmt"
      "net/http"
      "time"
    )
    
    func main() {
      http.HandleFunc("/hello", timed(hello))
      http.ListenAndServe(":3000", nil)
    }
    
    func timed(f func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
      return func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        f(w, r)
        end := time.Now()
        fmt.Println("The request took", end.Sub(start))
      }
    }
    
    func hello(w http.ResponseWriter, r *http.Request) {
      fmt.Fprintln(w, "<h1>Hello!</h1>")
    }

    注意,我们的timing()函数接受一个可以用作处理函数的函数,并返回一个类型相同的函数,但是返回的函数与传递它的函数不同。返回的闭包记录当前时间,调用原始函数,最后记录结束时间并打印出请求的持续时间。同时不知道处理函数内部实际发生了什么。

    3.访问通常不可用的数据

    虽然这使用了我们在本文前面看到的技术,但值得指出它本身,因为它非常有用。

    闭包还可以用于将数据包装在函数内部,否则该函数通常不能访问该数据。例如,如果要在不使用全局变量的情况下提供对数据库的处理程序访问,则可以编写如下代码。

    package main
    
    import (
      "fmt"
      "net/http"
    )
    
    type Database struct {
      Url string
    }
    
    func NewDatabase(url string) Database {
      return Database{url}
    }
    
    func main() {
      db := NewDatabase("localhost:5432")
    
      http.HandleFunc("/hello", hello(db))
      http.ListenAndServe(":3000", nil)
    }
    
    func hello(db Database) func(http.ResponseWriter, *http.Request) {
      return func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, db.Url)
      }
    }

    现在我们可以编写处理函数,就好像它们可以访问一个Database对象,同时仍然返回一个具有http.HandleFunc()期望签名的函数这允许我们绕过http.HandleFunc()不允许我们传递自定义变量而不诉诸全局变量或任何类型的事实。

    4.使用排序包进行二进制搜索

    在标准库中使用包时也经常需要关闭,例如排序包。

    这个包为我们提供了大量有用的函数和代码,用于排序和搜索排序列表。例如,如果要对整数切片进行排序,然后在切片中搜索数字7,则可以sort像这样使用包。

    package main
    
    import (
      "fmt"
      "sort"
    )
    
    func main() {
      numbers := []int{1, 11, -5, 7, 2, 0, 12}
      sort.Ints(numbers)
      fmt.Println("Sorted:", numbers)
      index := sort.SearchInts(numbers, 7)
      fmt.Println("7 is at index:", index)
    }

    但是如果要搜索每个元素是自定义类型的切片会发生什么?或者,如果要查找第一个数字7或更高的索引,而不仅仅是第一个7的索引?

    为此,您需要使用sort.Search()函数,并且需要传入一个闭包,该闭包可用于确定特定索引处的数字是否符合您的条件。

    5.推迟工作

  • 相关阅读:
    DataTables合并单元格(rowspan)的实现思路(多分组分类的情况)
    DataTables固定表格宽度(设置横向滚动条)
    用图片替代cursor光标样式
    DataTables获取指定元素的行数据
    任意表格(table)实现拖动列(column)改变列大小
    鼠标拖动改变DIV等网页元素的大小的最佳实践
    DataTables实现rowspan思路
    DataTables添加额外的查询参数和删除columns等无用参数
    击穿(强推---神仙代码)
    java jvm 参数 -Xms -Xmx -Xmn -Xss 调优总结
  • 原文地址:https://www.cnblogs.com/xiaodoujiaohome/p/10530083.html
Copyright © 2011-2022 走看看