0
点赞
收藏
分享

微信扫一扫

Go编程之函数汇总


Golang之函数

一、函数定义

Go语言中支持函数、匿名函数和闭包,下面看一下函数的定义

// 第一个小括号当中是参数列表,
// 第二个小括号是返回值列表
func A(a int, b string) (int, string, int) {

}

// 如果abc都是int型的话,可以按照这种方法进行简写,同样的方法也适用于返回值当中
func A(a, b, c int) (int, string, int) {
}

// 无返回值
func A() {}

命名返回值

func a(x, y int) (a, b int) {
a = x + y
b = x - y
return
}

func TestOne(t *testing.T) {
t.Log(a(12, 4)) // 16, 8
}

二、可变参数

import "testing"

func Snm(ops ...int) int {
ret := 0
for _, op := range ops {
ret += op
}
return ret
}

func TestTimeSpent(t *testing.T) {
t.Log(Snm(1, 3, 5, 7, 9))
}

三、函数类型和变量

使用type来定义的函数类型

// 定义了一个calc类型,它是一种函数类型,
// 这种函数接收两个int类型的参数,返回一个int类型的返回值。
type calcu func(int, int) int

func add(a, b int) int {
return a + b
}
// 使用
func TestFuncOne(t *testing.T) {
var c calcu
c = add
t.Log(c(12, 45))
}

四、函数作为参数

type calcu func(int, int) int

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

// 函数最为参数,可以直接上面定义的函数类型
func sum(a, b int, c calcu) int {
return c(a, b)
}
// 也可以使用下面这种,这两个使用其实是一样的
func sum1(a, b int, c func(int, int) int) int {
return c(a, b)
}

func TestFuncTwo(t *testing.T) {
res1 := sum(1, 2, add)
res2 := sum1(1, 2, add)
t.Log(res1)
t.Log(res2)
}

五、函数作为返回值

type calcu func(int, int) int

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

func sub(a, b int) int {
return a - b
}

func selectOption(s string) (calcu, error) {
switch s {
case "+":
return add, nil
case "-":
return sub, nil
default:
err := errors.New("未知函数")
return nil, err
}
}

func TestFuncThree(t *testing.T) {
option, err := selectOption("/")
if err != nil {
panic(err)
}
t.Log(option(1, 2))
}

六、匿名函数

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

6.1. 匿名函数的使用方式:

func TestFuncFour(t *testing.T) {
// 在定义匿名函数的时候就可以直接使用(这种方式只使用一次)
func1 := func(a, b int) int {
return a + b
} (1, 2)
t.Log(func1)

// 将匿名函数赋给一个变量(函数变量),再通过该变量来调用匿名函数
add := func(a, b int) int {
return a + b
}
func2 := add(1, 2)
t.Log(func2)
}

6.2 全局匿名函数

全局匿名函数就是将匿名函数赋给一个全局变量,那么这个匿名函数在当前程序里可以使用

var (
// Add 就是定义好的全局变量
// 全局变量必须首字母大写
Add = func(a, b int) int {
return a + b
}
)

func main() {
fmt.Println(add(1, 3))
}

七、函数闭包

闭包是匿名函数与匿名函数所引用环境的组合。匿名函数有动态创建的特性,该特性使得匿名函数不用通过参数传递的方式,就可以直接引用外部的变量。

7.1. 闭包作为函数返回值

func Increase() func () int {
n := 0
return func() int {
n++
return n
}
}

func TestFuncFive(t *testing.T) {
out := Increase()
t.Log(out())
t.Log(out())
t.Log(out())
}

7.2. 返回多个内部函数

func calc1(a int) (func(int) int, func(int) int) {
add := func(i int) int {
a += i
return a
}

sub := func(i int) int {
a -= i
return a
}

return add, sub
}

func TestSix(t *testing.T) {
f1, f2 := calc1(100)
t.Log(f1(1), f2(2)) // 101 99
t.Log(f1(1), f2(2)) // 100 98
t.Log(f1(2), f2(3)) // 100 97
}

其他的使用在golang的并发中在介绍吧

八、延迟函数defer

Go语言中的​​defer​​​语句会将其后面跟随的语句进行延迟处理。在​​defer​​​归属的函数即将返回时,将延迟处理的语句按​​defer​​​定义的逆序进行执行,也就是说,先被​​defer​​​的语句最后被执行,最后被​​defer​​​的语句,最先被执行。
​​​defer​​ 通常用于简化函数的各种各样清理动作,例如关闭文件,解锁等等的释放资源的动作。

func TestSeven(t *testing.T) {
defer fmt.Println("No.0")
fmt.Println("start")
defer fmt.Println("No.1")
defer fmt.Println("No.2")
fmt.Println("end")
defer fmt.Println("No.3")
}
// 输入结果
start
end
No.3
No.2
No.1
No.0

如果返回值是无名的, ​​func test() int {}​​​ 则go会在return 的时候创建一个临时的变量​​s​​​保存return值的动作,之后的​​defer​​​函数不能操作临时变量​​s​​​ ,只能操作变量​​i​​。

func test() int {
var i int
defer func() {
i++
fmt.Println("defer1 = >", i)
}()

defer func() {
i++
fmt.Println("defer2 = >", i)
}()

return i
}

func TestFuncTen(t *testing.T) {
t.Log(test())
}

// 输出结果
defer2 = > 1
defer1 = > 2
func_test.go:162: 0

如果返回值是有名的(eg : ​​func test() (i int ) {}​​​) 那么在执行return的时候,就不会创造临时变量去保存​​i​​​,之后的​​defer​​​函数可以操作​​i​​。

func test1() (i int) {
defer func() {
i++
fmt.Println("defer1 = >", i)
}()

defer func() {
i++
fmt.Println("defer2 = >", i)
}()

return i
}

func TestFuncTen(t *testing.T) {
t.Log(test1())
}

// 输出结果
defer2 = > 1
defer1 = > 2
func_test.go:164: 2

在Go语言的函数中​​return​​​语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。而​​defer​​​语句执行的时机就在返回值赋值操作后,RET指令执行前。
另外 ​​​defer​​​ 函数会先于 ​​panic​​ 之前执行。

func TestDefer(t *testing.T) {
defer func() {
t.Log("Clear resources")
}()
t.Log("Started")
panic("Fail error")
}


结果:
=== RUN TestDefer
func_test.go:22: Started
func_test.go:20: Clear resources
--- FAIL: TestDefer (0.00s)
panic: Fail error [recovered]
panic: Fail error

九、内置函数

名称

作用

close

用来关闭channel

len

用来求长度

cap

用来返回某一个类型最大容量(只用于切片和map)

new

用来分配内存,主要用来分配值类型,例如int,struct,返回的是指针

make

用来分配内存,主要用来分配应用类型,例如:chan, map, slice

copy

用来复制元素

append

用来追加元素到数组,slice中

panic/recover

用于错误处理机制

print/println

底层打印函数,在项目中建议使用fmt包

complex/real imag

用于操作和处理复数

十、 ​​panic/recover​

这里需要强调下:​​panic​​​可以在任何地方引发,但​​recover​​​只有在​​defer​​调用的函数中有效。

10.1 ​​panic​

​panic​​​ 是内建的停止控制流的函数。相当于其他编程语言的抛异常操作。当函数F调用了 ​​panic​​​ ,F的执行会被停止,在F中 ​​panic​​​ 前面定义的 ​​defer​​​ 操作都会被执行,然后F函数返回。对于调用者来说,调用F的行为就像调用 ​​panic​​​ (如果F函数内部没有把 ​​panic recover​​​ 掉)。如果都没有捕获该 ​​panic​​​ ,相当于一层层 ​​panic​​​ ,程序将会 ​​crash​​​ 。 ​​panic​​ 可以直接调用,也可以是程序运行时错误导致,例如数组越界。

10.2 ​​recover​

​recover​​​ 是一个从 ​​panic​​​ 恢复的内建函数。 ​​recover​​​ 只有在 ​​defer​​​ 的函数里面才能发挥真正的作用。如果是正常的情况(没有发生 ​​panic​​​ ),调用 ​​recover​​​ 将会返回 ​​nil​​​ 并且没有任何影响。如果当前的 ​​goroutine panic​​​ 了, ​​recover​​​ 的调用将会捕获到 ​​panic​​ 的值,并且恢复正常执行。

func funcA() {
fmt.Println("func A")
}

func funcB() {
defer func() {
err := recover()
//如果程序出出现了panic错误,可以通过recover恢复过来
if err != nil {
fmt.Println("recover in B")
}
}()
panic("panic in B")
}

func funcC() {
fmt.Println("func C")
}
func main() {
funcA()
funcB()
funcC()
}

// 输出
func A
recover in B
func C

使用注意: ​​recover()​​​  必须搭配 ​​defer​​​  使用; ​​defer​​​  一定要在可能引发 ​​panic​​  的语句前面定义。


举报

相关推荐

0 条评论