关于golang:golang函数总结

52次阅读

共计 7372 个字符,预计需要花费 19 分钟才能阅读完成。

golang 函数

本文视频教程:https://www.bilibili.com/vide…

关注公众号,支付课程材料及源码

golang 函数简介

函数的 go 语言中的 一级公民,咱们把所有的性能单元都定义在函数中,能够重复使用。函数蕴含函数的名称、参数列表和返回值类型,这些形成了函数的签名(signature)。

go 语言中函数个性

  1. go 语言中有 3 种函数:一般函数、匿名函数(没有名称的函数)、办法(定义在 struct 上的函数)。receiver
  2. go 语言中不容许函数重载(overload),也就是说不容许函数同名。
  3. go 语言中的函数不能嵌套函数,但能够嵌套匿名函数。
  4. 函数是一个值,能够将函数赋值给变量,使得这个变量也成为函数。
  5. 函数能够作为参数传递给另一个函数。
  6. 函数的返回值能够是一个函数。
  7. 函数调用的时候,如果有参数传递给函数,则先拷贝参数的正本,再将正本传递给函数。
  8. 函数参数能够没有名称。

go 语言中函数的定义和调用

函数在应用之前必须先定义,能够调用函数来实现某个工作。函数能够反复调用,从而达到代码重用。

go 语言函数定义语法

func function_name([parameter list] ) [return_types]
{函数体}

语法解析:

  • func:函数由 func 开始申明
  • function_name:函数名称,函数名和参数列表一起形成了函数签名。
  • [parameter list]:参数列表,参数就像一个占位符,当函数被调用时,你能够将值传递给参数,这个值被称为理论参数。参数列表指定的是参数类型、程序、及参数个数。参数是可选的,也就是说函数也能够不蕴含参数。
  • return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些性能不须要返回值,这种状况下 return_types 不是必须的。
  • 函数体:函数定义的代码汇合。

go 语言函数定义实例

定义一个求和函数

func sum(a int, b int) (ret int) {
    ret = a + b
    return ret
}

定义一个比拟两个数大小的函数

func compare(a int, b int) (max int) {
    if a > b {max = a} else {max = b}
    return max
}

go 语言函数调用

当咱们要实现某个工作时,能够调用函数来实现。调用函数要传递参数,如何有返回值能够取得返回值。

func main() {s := sum(1, 2)
    fmt.Printf("s: %v\n", s)

    max := compare(1, 2)
    fmt.Printf("max: %v\n", max)
}

运行后果

s: 3
max: 2

golang 函数的返回值

函数能够有 0 或多个返回值,返回值须要指定数据类型,返回值通过 return 关键字来指定。

return能够有参数,也能够没有参数,这些返回值能够有名称,也能够没有名称。go 中的函数能够有多个返回值。

  1. return关键字中指定了参数时,返回值能够不必名称。如果 return 省略参数,则返回值局部必须带名称
  2. 当返回值有名称时,必须应用括号突围,逗号分隔,即便只有一个返回值
  3. 但即便返回值命名了,return中也能够强制指定其它返回值的名称,也就是说 return 的优先级更高
  4. 命名的返回值是事后申明好的,在函数外部能够间接应用,无需再次申明。命名返回值的名称不能和函数参数名称雷同,否则报错提醒变量反复定义
  5. return中能够有表达式,但不能呈现赋值表达式,这和其它语言可能有所不同。例如 return a+b 是正确的,但 return c=a+b 是谬误的。

go 语言函数返回值实例

没有返回值

func f1() {fmt.Printf("我没有返回值,只是进行一些计算")
}

有一个返回值

func sum(a int, b int) (ret int) {
    ret = a + b
    return ret
}

多个返回值,且在 return 中指定返回的内容

func f2() (name string, age int) {
    name = "老郭"
    age = 30
    return name, age
}

多个返回值,返回值名称没有被应用

func f3() (name string, age int) {
    name = "老郭"
    age = 30
    return // 等价于 return name, age
}

return 笼罩命名返回值,返回值名称没有被应用

func f4() (name string, age int) {
    n := "老郭"
    a := 30
    return n, a
}

Go 中常常会应用其中一个返回值作为函数是否执行胜利、是否有错误信息的判断条件。例如 return value,existsreturn value,okreturn value,err 等。

当函数的 返回值过多 时,例如有 4 个以上的返回值,应该将这些返回值收集到容器中,而后以返回容器的形式去返回。例如,同类型的返回值能够放进 slice 中,不同类型的返回值能够放进 map 中。

但函数有多个返回值时,如果其中某个或某几个返回值不想应用,能够通过下划线 _ 来抛弃这些返回值。例如上面的 f1 函数两个返回值,调用该函数时,抛弃了第二个返回值 b,只保留了第一个返回值 a 赋值给了变量a

package main

import "fmt"

func f1() (int, int) {return 1, 2}
func main() {_, x := f1()
    fmt.Printf("x: %v\n", x)
}

运行后果

x: 2

golang 函数的参数

go 语言函数能够有 0 或多个参数,参数须要指定 数据类型

申明函数时的参数列表叫做形参,调用时传递的参数叫做实参。

go 语言是通过 传值的形式传参 的,意味着传递给函数的是拷贝后的正本,所以函数外部拜访、批改的也是这个正本。

go 语言能够应用 变长参数 ,有时候并不能确定参数的个数,能够应用变长参数,能够在函数定义语句的参数局部应用ARGS...TYPE 的形式。这时会将 ... 代表的参数全副保留到一个名为 ARGS 的 slice 中,留神这些参数的数据类型都是 TYPE。

go 语言函数的参数实例

go 语言传参

// 形参列表
func f1(a int, b int) int {
    if a > b {return a} else {return b}
}

func main() {
    // 实参列表
    r := f1(1, 2)
    fmt.Printf("r: %v\n", r)
}

演示参数传递,按值传递

func f1(a int) {
    a = 200
    fmt.Printf("a1: %v\n", a)
}

func main() {
    a := 100
    f1(a)
    fmt.Printf("a: %v\n", a)
}

运行后果

a1: 200
a: 100

从运行后果能够看到,调用函数 f1 后,a 的值并没有被扭转,阐明参数传递是拷贝了一个正本,也就是拷贝了一份新的内容进行运算。

mapsliceinterfacechannel这些数据类型自身就是 指针 类型的,所以就算是拷贝传值也是拷贝的指针,拷贝后的参数依然指向底层数据结构,所以批改它们 可能 会影响内部数据结构的值。

package main

import "fmt"

func f1(a []int) {a[0] = 100
}

func main() {a := []int{1, 2}
    f1(a)
    fmt.Printf("a: %v\n", a)
}

运行后果

a: [1 2]
a: [100 2]

从运行后果发现,调用函数后,slice 内容被扭转了。

变长参数

package main

import "fmt"

func f1(args ...int) {
    for _, v := range args {fmt.Printf("v: %v\n", v)
    }
}
func f2(name string, age int, args ...int) {fmt.Printf("name: %v\n", name)
    fmt.Printf("age: %v\n", age)
    for _, v := range args {fmt.Printf("v: %v\n", v)
    }
}
func main() {f1(1, 2, 3)
    fmt.Println("------------")
    f1(1, 2, 3, 4, 5, 6)
    fmt.Println("------------")
    f2("tom", 20, 1, 2, 3)
}

运行后果

v: 1
v: 2
v: 3
------------
v: 1
v: 2
v: 3
v: 4
v: 5
v: 6
------------
name: tom
age: 20
v: 1
v: 2
v: 3

golang 函数类型与函数变量

能够应用 type 关键字来定义一个函数类型,语法格局如下:

type fun func(int, int) int

下面语句定义了一个 fun 函数类型,它是一种函数类型,这种函数接管两个 int 类型的参数,并且返回一个 int 类型的返回值。

上面咱们定义两个这样构造的两个函数,一个求和,一个比拟大小:

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

func max(a int, b int) int {
    if a > b {return a} else {return b}
}

上面定义一个 fun 函数类型,把 summax赋值给它

package main

import "fmt"

type fun func(int, int) int

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

func max(a int, b int) int {
    if a > b {return a} else {return b}
}

func main() {
    var f fun
    f = sum
    s := f(1, 2)
    fmt.Printf("s: %v\n", s)
    f = max
    m := f(3, 4)
    fmt.Printf("m: %v\n", m)
}

运行后果

s: 3
m: 4

golang 高阶函数

go 语言的函数,能够作为函数的参数,传递给另外一个函数,能够能够作为,另外一个函数的返回值返回。

go 语言函数作为参数

package main

import "fmt"

func sayHello(name string) {fmt.Printf("Hello,%s", name)
}

func f1(name string, f func(string)) {f(name)
}

func main() {f1("tom", sayHello)
}

运行后果

Hello,tom

go 语言函数作为返回值

package main

import "fmt"

func add(x, y int) int {return x + y}

func sub(x, y int) int {return x - y}

func cal(s string) func(int, int) int {
    switch s {
    case "+":
        return add
    case "-":
        return sub
    default:
        return nil
    }
}

func main() {add := cal("+")
    r := add(1, 2)
    fmt.Printf("r: %v\n", r)

    fmt.Println("-----------")

    sub := cal("-")
    r = sub(100, 50)
    fmt.Printf("r: %v\n", r)
}

运行后果

r: 3
-----------
r: 50

golang 匿名函数

go 语言函数不能嵌套,然而在函数外部能够定义匿名函数,实现一下简略性能调用。

所谓匿名函数就是,没有名称的函数。

语法格局如下:

func (参数列表)(返回值)

当然能够既没有参数,能够没有返回值

匿名函数实例

func main() {max := func (a int, b int) int {
        if a > b {return a} else {return b}
    }

    i := max(1, 2)
    fmt.Printf("i: %v\n", i)
}

运行后果

i: 2

本人执行

func main() {
    // 本人执行
    func(a int, b int) {
        max := 0
        if a > b {max = a} else {max = b}
        fmt.Printf("max: %v\n", max)
    }(1, 2)
}

运行后果

max: 2

golang 闭包

闭包能够了解成 定义在一个函数外部的函数。在实质上,闭包是将函数外部和函数内部连接起来的桥梁。或者说是函数和其援用环境的组合体。

闭包指的是一个函数和与其相干的援用环境组合而成的实体。简略来说,闭包 = 函数 + 援用环境。首先咱们来看一个例子:

package main

import "fmt"

// 返回一个函数
func add() func(int) int {
    var x int
    return func(y int) int {
        x += y
        return x
    }
}

func main() {var f = add()
    fmt.Println(f(10))
    fmt.Println(f(20))
    fmt.Println(f(30))
    fmt.Println("-----------")
    f1 := add()
    fmt.Println(f1(40))
    fmt.Println(f1(50))
}

运行后果

10
30
60
-----------
40
90

变量 f 是一个函数并且它援用了其内部作用域中的 x 变量,此时 f 就是一个闭包。在 f 的生命周期内,变量 x 也始终无效。 闭包进阶示例 1:

package main

import "fmt"

func add(x int) func(int) int {return func(y int) int {
        x += y
        return x
    }
}
func main() {var f = add(10)
    fmt.Println(f(10))
    fmt.Println(f(20))
    fmt.Println(f(30))

    fmt.Println("----------")

    f1 := add(20)
    fmt.Println(f1(40))
    fmt.Println(f1(50))
}

运行后果

20
40
70
----------
60
110

闭包进阶示例 2:

func makeSuffixFunc(suffix string) func(string) string {return func(name string) string {if !strings.HasSuffix(name, suffix) {return name + suffix}
        return name
    }
}

func main() {jpgFunc := makeSuffixFunc(".jpg")
    txtFunc := makeSuffixFunc(".txt")
    fmt.Println(jpgFunc("test")) 
    fmt.Println(txtFunc("test")) 
}

运行后果

test.jpg
test.txt

闭包进阶示例 3:

func calc(base int) (func(int) int, func(int) int) {add := func(i int) int {
        base += i
        return base
    }

    sub := func(i int) int {
        base -= i
        return base
    }
    return add, sub
}

func main() {f1, f2 := calc(10)
    fmt.Println(f1(1), f2(2)) 
    fmt.Println(f1(3), f2(4)) 
    fmt.Println(f1(5), f2(6)) 
}

运行后果

11 9
12 8
13 7

闭包其实并不简单,只有牢记 闭包 = 函数 + 援用环境

golang 递归

函数外部调用函数本身的函数称为递归函数。

应用递归函数最重要的三点:

  1. 递归就是本人调用本人。
  2. 必须先定义函数的退出条件,没有退出条件,递归将成为死循环。
  3. go 语言递归函数很可能会产生一大堆的 goroutine,也很可能会呈现栈空间内存溢出问题。

go 语言递归实例

阶乘

package main

import "fmt"

func a(n int) int {
    // 返回条件
    if n == 1 {return 1} else {
        // 本人调用本人
        return n * a(n-1)
    }
}

func main() {
    n := 5
    r := a(n)
    fmt.Printf("r: %v\n", r)
}

运行后果

r: 120

斐波那契数列

它的计算公式为 f(n)=f(n-1)+f(n-2)f(2)=f(1)=1

package main

func f(n int) int {
    // 退出点判断
    if n == 1 || n == 2 {return 1}
    // 递归表达式
    return f(n-1) + f(n-2)
}

func main() {r := f(5)
    fmt.Printf("r: %v\n", r)
}

运行后果

r: 5

golang defer 语句

go 语言中的 defer 语句会将其前面追随的语句进行 提早 解决。在 defer 归属的函数行将返回时,将提早解决的语句按 defer 定义的 逆序 进行执行,也就是说,先被 defer 的语句最初被执行,最初被 defer 的语句,最先被执行。stack

defer 个性

  1. 关键字 defer 用于注册提早调用。
  2. 这些调用直到 return 前才被执。因而,能够用来做资源清理。
  3. 多个 defer 语句,按先进后出的形式执行。
  4. defer语句中的变量,在 defer 申明时就决定了。

defer 用处

  1. 敞开文件句柄
  2. 锁资源开释
  3. 数据库连贯开释

go 语言 defer 语句实例

查看执行程序

func main() {fmt.Println("start")
    defer fmt.Println("step1")
    defer fmt.Println("step2")
    defer fmt.Println("step3")
    fmt.Println("end")
}

运行后果

start
end
step3
step2
step1

golang init 函数

golang 有一个非凡的函数 init 函数,先于 main 函数执行,实现包级别的一些 初始化 操作。

init 函数的次要特点

  • init 函数先于 main 函数 主动执行,不能被其余函数调用;
  • init 函数没有输出参数、返回值;
  • 每个包能够有多个 init 函数;
  • 包的每个源文件也能够有多个 init 函数,这点比拟非凡;
  • 同一个包的 init 执行程序,golang 没有明确定义,编程时要留神程序不要依赖这个执行程序。
  • 不同包的 init 函数依照包导入的依赖关系决定执行程序。

golang 初始化程序

初始化程序:变量初始化 ->init()->main()

实例

package main

import "fmt"

var a int = initVar()

func init() {fmt.Println("init2")
}

func init() {fmt.Println("init")
}

func initVar() int {fmt.Println("init var...")
    return 100
}

func main() {fmt.Println("main...")
}

运行后果

init var...
init2
init
main...

正文完
 0