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  的语句前面定义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值