GO 从 0 到 1 系列:8 函数类型+匿名函数+闭包

函数类型+匿名函数+闭包

函数也可以赋值给变量,存储在数组、切片、映射中,也可作为参数传递给函数或作为函数返回值进行返回

通过函数类型,我们也可以定义一些集合类型,比如切片的映射的

函数类型的意义:

函数类型可以把函数作为参数传递到其他的函数中

1 函数类型

函数类型:由参数的数量和他的类型,还有返回值的数量和类型组成

package main

import "fmt"

func add(a, b int) int {
    return a + b
}

func main() {
    c := add
    fmt.Printf("%T\n", c)
}

通过输出我们可以看到,函数本身也是一个类型

[17:04:03 root@go day1]#go run main.go 
func(int, int) int

1.1 定义函数类型

package main

import "fmt"

func main() {

    // 定义函数类型
    var f func(int, int) int
    fmt.Printf("%T\n", f)
}
[15:01:56 root@go day1]#go run main.go 
func(int,int) int

这个函数类型的 f 有什么用呢,我们就可以将 add 函数赋值给 f,f 就可以调用 add 函数中的形参和返回值,前提是 f 这个函数类型的形参和返回值要对应 add 函数的类型

package main

import "fmt"

func add(a, b int) int {
    return a + b
}

func main() {

    // 定义函数类型
    var f func(int, int) int

    // 将 add 函数赋值给 f 
    f = add

    fmt.Printf("type = %T\nvalue = %v\n", f, f(1, 2))
}
[15:28:20 root@go day1]#go run main.go 
type = func(int, int) int
value = 3

也可以这样赋值

package main

import "fmt"

func add(a, b int) int {
    return a + b
}

func main() {

    // 定义函数类型
    var f func(int, int) int = add
    fmt.Printf("type = %T\nvalue = %v\n", f, f(1, 2))
}
[15:28:20 root@go day1]#go run main.go 
type = func(int, int) int
value = 3

1.1.1 定义函数切片

package main

import "fmt"

func add(a, b int) int {
    return a + b
}

func mul(a, b int) int {
    return a * b
}

func main() {
    // 定义函数切片
    var fs []func(int, int) int

    // append 追加 add 和 mul 函数
    fs = append(fs, add, mul)

    // for-range 变量 fs ,由于 fs 是一个切片,所以 fs 的元素就是对应的每个函数
    // 将 fs 的元素赋值给 f
    for _, f := range fs {

        // 给 f 这个函数传递 2,3
        // 第一次调用 add 函数
        // 第二次调用 mul 函数
        fmt.Println(f(2, 3))
    }
}
[15:29:06 root@go day1]#go run main.go 
5
6

1.1.2 声明&调用参数类型为函数的函数

函数类型
    func(参数类型列表) 返回值类型列表 

传递一个函数到另一个函数中的时候,形参函数的类型、返回值与传递函数的类型、返回值要相同

函数形参的定义不需要定义名字,只需要定义函数的类型

package main

import "fmt"

// print 函数形参为 fmts 函数类型为 func(string) string
// args 可变参数的 string 类型
func print(fmts func(string) string, args ...string) {
    // 遍历 args 取出对应的值
    for i, v := range args {

        // 再将 args 的值取出来传递给 fmts 函数
        fmt.Println(i, fmts(v))
    }
}

// 定义 star 函数类型维 func(string) string
func star(txt string) string {
    return "*" + txt + "*"
}

func main() {

    // 定义 names 切片
    names := []string{"zz", "kk", "17"}

    // 调用 print 函数,传入 start 函数和 names 切片解包后的值
    // star 传给 fmts 函数
    // names... 传递给 args
    print(star, names...)
}
[15:31:38 root@go day1]#go run main.go 
0 *zz*
1 *kk*
2 *17*

1.1.3 函数返回值是一个函数类型

作为返回值的时候,需要返回的函数类型和返回值相等

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func sayHi() {
    fmt.Println("Hi")
}

func sayHello() {
    fmt.Println("hello")
}

// 返回值为 func() 函数
func genFunc() func() {

    // 随机数取模 2 == 0 时候 return sayHi ,否则 return sayHello
    if rand.Int()%2 == 0 {
        return sayHi
    } else {
        return sayHello
    }

}

func main() {
    // 种子
    rand.Seed(time.Now().UnixNano())
    f := genFunc()
    f()
}
[16:12:38 root@go day1]#go run main.go 
Hi

范例二:

package main

import "fmt"

// 定义返回值为 func(int, int) int 
func test(srt string) func(int, int) int {
    fmt.Print(srt)
    return intr
}

func intr(a, b int) int {
    return a + b
}

func main() {

    // f 为 func(int, int) int ,接收 test 函数返回值
    f := test("值等于:")
    fmt.Println(f(1, 2))
}
[16:16:40 root@go day1]#go run main.go 
值等于
3

范例三:

package main

import "fmt"

func print(fmts func(string) string, args ...string) func(int, int) int {
    for i, v := range args {
        fmt.Println(i, fmts(v))
    }
    return intr
}

func star(txt string) string {
    return "♥" + txt + "♥"
}

func intr(a, b int) int {
    fmt.Println("intr 函数结果:")
    return a + b
}

func main() {
    names := []string{"77", "拦截", "zz"}

    // 返回值函数赋值给 res = func(int, int) int
    // 并且 print 函数里面的代码也会执行
    res := print(star, names...)

    // 输出的时候给 res 赋值 1,2
    fmt.Println(res(1, 2))
}
[16:29:44 root@go day1]#go run main.go 
0 ♥77♥
1 ♥拦截♥
2 ♥zz♥
intr 函数结果:
3

通过调用函数,实现字符分割

package main

import (
    "fmt"
    "strings"
)

// 定义 函数 aFields 类型为 func(rune) bool
func aFields(split rune) bool {
    if split == 'a' {
        return true
    } else {
        return false
    }

}

func main() {

    // 输出 strings.FieldsFunc 函数,第一个参数为 string ,第二个参数必须为 func(rune) bool
    fmt.Printf("%q\n", strings.FieldsFunc("abcdef", aFields))
}

把 a 进行分割

[16:43:40 root@go day1]#go run main.go 
["bcdef"]

当然这个代码还能够优化

很多时候代码会写得很长,但是我们只需要一句话就可以将代码写完

package main

import (
    "fmt"
    "strings"
)

func aFields(split rune) bool {

    // 由于返回值为 bool 类型,所以这里直接 split == a 默认就为 真,否则为 false
    return split == 'a'
}

func main() {
    fmt.Printf("%q\n", strings.FieldsFunc("abcdef", aFields))
}
[16:43:40 root@go day1]#go run main.go 
["bcdef"]

也可以简写成匿名函数

package main

import (
    "fmt"
    "strings"
)

func main() {
    fmt.Printf("%q\n", strings.FieldsFunc("abcdef",
        func(split rune) bool {
            return split == 'a'
        }))
}
[16:43:40 root@go day1]#go run main.go 
["bcdef"]

1.2 匿名函数

不需要定义名字的函数叫做匿名函数,常用帮助函数在局部代码块中使用或作为其他函数的参数

匿名函数是没有名字,一般用来赋值给变量的,只能使用在函数内部

1.2.1 匿名函数定义

很多时候我们定义完了函数只需要使用一次,不具有代码的复用性,当我们临时使用的时候就可以使用匿名函数

package main

import "fmt"

func main() {

    // 定义一个 c 类型为 func()
    c := func() {
    }
    fmt.Printf("%T\n", c)
}

通过输出我们会发现 c 是一个 func() 类型

[16:51:51 root@go day1]#go run main.go 
func()

匿名函数赋值和使用

package main

import "fmt"

func main() {
    // 定义函数,变量为 c 
    c := func() {
        fmt.Println("我是匿名函数")
    }

    // 使用 c 函数
    c()
    fmt.Printf("%T\n", c)
}
[17:01:18 root@go day1]#go run main.go 
我是匿名函数
func()

使用范例二

package main

import "fmt"

func print(fmts func(string) string, args ...string) {
    for i, v := range args {
        fmt.Println(i, fmts(v))
    }
}

func main() {
    names := []string{"77", "kk", "zz"}

    // 定义 star 匿名函数,类型为 func(string) string
    // star 标识符只能在 main 函数内部使用
    star := func(txt string) string {
        return "*" + txt + "*"
    }

    // 传递给 print 函数
    print(star, names...)

}
[17:02:18 root@go day1]#go run main.go 
0 *77*
1 *kk*
2 *zz*

简写直接函数传递

package main

import "fmt"

func print(fmts func(string) string, args ...string) {
    for i, v := range args {
        fmt.Println(i, fmts(v))
    }
}

func main() {
    names := []string{"77", "kk", "zz"}

    // 传递给 print 函数
    // 直接传递 函数 func(s string) string { return "*" + s + "*" }
    print(func(s string) string {
        return "*" + s + "*"
    }, names...)

}
[17:02:18 root@go day1]#go run main.go 
0 *77*
1 *kk*
2 *zz*

1.2.3 函数直接调用

匿名函数直接使用

package main

import "fmt"

func main() {
    // 由于这个变量没有使用变量来接收,所以需要使用 () 自我调用
    func() {
        fmt.Println("我是匿名函数")
    }() // 后面加上 () 表示直接调用
}
[17:43:55 root@go day1]#go run main.go 
我是匿名函数

1.3 闭包

匿名函数又叫闭包, 是指在函数内定义的匿名函数引用外部函数的变量,只要匿名函数继续使用则外部函数赋值的变量不被自动销毁

如果使用不严谨的话会导致内存泄漏

addBase : 函数执行完以后,这个 addBase 函数中的 base 应该释放掉,函数内部引用函数外部的变量,使函数外部的变量生命周期发生了变化这就是闭包

变量生命周期:指的是变量生成到死亡的时间

package main

import "fmt"

// 生成一个函数
// 定义 addBase(int) func(int) int
// 返回值函数 num + base
func addBase(base int) func(int) int {

    // 在函数内部引用函数外部的变量
    // 当我们内部的函数返回的时候,其实外部的这个 base 变量不能及时销毁
    // 所以我们在返回函数里面就要添加 base 变量

    return func(num int) int {
        return base + num
    }
}
func main() {

    // add2 =  func(int) int
    add2 := addBase(2)
    fmt.Printf("%T\n", add2)

    fmt.Println(add2(10))
}
[17:46:26 root@go day1]#go run main.go 
12
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇