北京大型网站开发,科技小制作视频,小白node怎么做网站,powershell做网站Go 语言支持普通函数、匿名函数和闭包#xff0c;从设计上对函数进行了优化和改进#xff0c;让函数使用起来更加方便。
Go 语言的函数属于“一等公民”#xff08;first-class#xff09;#xff0c;也就是说#xff1a;
函数本身可以作为值进行传递。支持匿名函数和闭… Go 语言支持普通函数、匿名函数和闭包从设计上对函数进行了优化和改进让函数使用起来更加方便。
Go 语言的函数属于“一等公民”first-class也就是说
函数本身可以作为值进行传递。支持匿名函数和闭包closure。函数可以满足接口。
函数定义: func function_name( [parameter list] ) [return_types] {函数体
}
func函数由 func 开始声明function_name函数名称函数名和参数列表一起构成了函数签名。parameter list参数列表参数就像一个占位符当函数被调用时你可以将值传递给参数这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的也就是说函数也可以不包含参数。return_types返回类型函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值这种情况下 return_types 不是必须的。函数体函数定义的代码集合。
返回值可以为多个
func test(x, y int, s string) (int, string) {// 类型相同的相邻参数参数类型可合并。 多返回值必须用括号。n : x y return n, fmt.Sprintf(s, n)
} 1.1 函数做为参数
函数做为一等公民可以做为参数传递。
func fn() int {return 300
}
func test(fn func() int) int {return fn()
}
func main() {//这是直接使用匿名函数s : test(func() int { return 200 })fmt.Println(s) //200//传入一个函数s2 : test(fn)fmt.Println(s2) //300
}在将函数做为参数的时候我们可以使用类型定义将函数定义为类型这样便于阅读
type myFunc func(s string, x, y int) stringfunc format(fn myFunc, s string, x, y int) string {return fn(s, x, y)
}func formatFunc(s string, x, y int) string {return fmt.Sprintf(s, x, y)
}
func main() {s2 : format(formatFunc, %d %d, 10, 20)fmt.Println(s2)
}1.2 函数返回值
函数返回值可以有多个同时Go支持对返回值命名
func main() {var a, b 10, 20fmt.Println(sum(a, b))
}
//多个返回值 用括号扩起来
func sum(a, b int) (int, int) {return a, b
}.3 参数
函数定义时指出函数定义时有参数该变量可称为函数的形参。
形参就像定义在函数体内的局部变量。
但当调用函数传递过来的变量就是函数的实参函数可以通过两种方式来传递参数
值传递指在调用函数时将实际参数复制一份传递到函数中这样在函数中如果对参数进行修改将不会影响到实际参数。
func swap(x, y int) int {... ...} 引用传递是指在调用函数时将实际参数的地址传递到函数中那么在函数中对参数所进行的修改将影响到实际参数。
package mainimport (fmt
)/* 定义相互交换值的函数 */
func swap(x, y *int) {*x,*y *y,*x
}func main() {var a, b int 1, 2/*调用 swap() 函数a 指向 a 指针a 变量的地址b 指向 b 指针b 变量的地址*/swap(a, b)fmt.Println(a, b)
} map、slice、chan、指针、interface默认以引用的方式传递。 不定参数传值
不定参数传值 就是函数的参数不是固定的后面的类型是固定的。可变参数
Golang 可变参数本质上就是 slice。只能有一个且必须是最后一个。
在参数赋值时可以不用用一个一个的赋值可以直接传递一个数组或者切片。
格式:
func test1(args ...int) { //0个或多个参数}func test2(a int, args ...int) int { //1个或多个参数}func test3(a int, b int, args ...int) int { //2个或多个参数}注意其中args是一个slice我们可以通过arg[index]依次访问所有参数,通过len(arg)来判断传递参数的个数. func main() {a : []int{10, 20, 10}fmt.Println(test(sum: %d, a...)) // slice... 展开slice
}func test(s string, n ...int) string {var x 0for _, v : range n {x v}return fmt.Sprintf(s, x)
}2. 匿名函数
匿名函数是指不需要定义函数名的一种函数实现方式。
在Go里面函数可以像普通变量一样被传递或使用Go语言支持随时在代码里定义匿名函数。
匿名函数由一个不带函数名的函数声明和函数体组成。匿名函数的优越性在于可以直接使用函数内的变量不必声明。
匿名函数的定义格式如下 func(参数列表)(返回参数列表){函数体
} func main() {//这里将一个函数当做一个变量一样的操作。getSqrt : func(a float64) float64 { return math.Sqrt(a)}fmt.Println(getSqrt(4.123))
} 在定义时调用匿名函数
匿名函数可以在声明后调用例如
func main() {func(a int) {fmt.Println(a)}(100) //(100)表示对匿名函数进行调用传递参数为 100。
}返回多个匿名函数
package mainimport fmtfunc FGen(x, y int) (func() int, func(int) int) {//求和的匿名函sum : func() int {return x y}// (xy) *z 的匿名函数avg : func(z int) int {return (x y) * z}return sum, avg
}func main() {f1, f2 : FGen(1, 2)fmt.Println(f1())fmt.Println(f2(3))
} 3. 闭包
所谓“闭包”指的是一个拥有许多变量和绑定了这些变量的环境的表达式通常是一个函数因而这些变量也是该表达式的一部分。
闭包函数引用环境 package mainimport fmtfunc main() {// 创建一个玩家生成器generator : playGen(码神)// 返回玩家的名字和血量name, hp : generator()// 打印值fmt.Println(name, hp)generator1 : playGen2()name1, hp1 : generator1(码神)// 打印值fmt.Println(name1, hp1)
}// 创建一个玩家生成器, 输入名称, 输出生成器
func playGen(s string) func() (string, int) {// 血量一直为150hp : 150// 返回创建的闭包return func() (string, int) {// 将变量引用到闭包中return s, hp}
}// 创建一个玩家生成器, 输入名称, 输出生成器
func playGen2() func(name string) (string, int) {hp : 150return func(name string) (string, int) {return name, hp}
}. 延迟调用
Go语言的 defer 语句会将其后面跟随的语句进行延迟处理
defer特性:
关键字 defer 用于注册延迟调用。这些调用直到 return 前才被执。因此可以用来做资源清理。多个defer语句按先进后出的方式执行。defer语句中的变量在defer声明时就决定了。
defer的用途
关闭文件句柄锁资源释放数据库连接释放
go 语言的defer功能强大对于资源管理非常方便但是如果没用好也会有陷阱。
package mainimport (logtime
)func main() {start : time.Now()log.Printf(开始时间为%v, start)defer log.Printf(时间差%v, time.Since(start)) // Now()此时已经copy进去了//不受这3秒睡眠的影响time.Sleep(3 * time.Second)log.Printf(函数结束)
}
Go 语言中所有的函数调用都是传值的调用 defer 关键字会立刻拷贝函数中引用的外部参数 包括start 和time.Since中的Nowdefer的函数在压栈的时候也会保存参数的值并非在执行时取值。
如何解决上述问题使用defer func()
func main() {start : time.Now()defer func() {log.Printf(开始调用defer)log.Printf(时间差%v, time.Since(start))log.Printf(结束调用defer)}()time.Sleep(3 * time.Second)log.Printf(函数结束)
}因为拷贝的是函数指针,函数属于引用传递
另一个问题
func main() {var whatever [5]int{1, 2, 3, 4, 5}for i, _ : range whatever {//函数正常执行,由于闭包用到的变量 i 在执行的时候已经变成4,所以输出全都是4.defer func() { fmt.Println(i) }()}
}解决
func main() {var whatever [5]int{1, 2, 3, 4, 5}for i, _ : range whatever {i : idefer func() { fmt.Println(i) }()}
}5. 异常处理
Go语言中使用 panic 抛出错误recover 捕获错误。
异常的使用场景简单描述Go中可以抛出一个panic的异常然后在defer中通过recover捕获这个异常然后正常处理。
panic
内置函数假如函数F中书写了panic语句会终止其后要执行的代码在panic所在函数F内如果存在要执行的defer函数列表按照defer的逆序执行返回函数F的调用者G在G中调用函数F语句之后的代码不会执行假如函数G中存在要执行的defer函数列表按照defer的逆序执行直到goroutine整个退出并报告错误
recover
内置函数用来捕获panic从而影响应用的行为
golang 的错误处理流程当一个函数在执行过程中出现了异常或遇到 panic()正常语句就会立即终止然后执行 defer 语句再报告异常信息最后退出 goroutine。如果在 defer 中使用了 recover() 函数,则会捕获错误信息使该错误信息终止报告。
注意:
利用recover处理panic指令defer 必须放在 panic 之前定义另外 recover 只有在 defer 调用的函数中才有效。否则当panic时recover无法捕获到panic无法防止panic扩散。recover 处理异常后逻辑并不会恢复到 panic 那个点去函数跑到 defer 之后的那个点。多个 defer 会形成 defer 栈后定义的 defer 语句会被最先调用。 func main() {test()
}func test() {defer func() {if err : recover(); err ! nil {log.Println(err) //2024/02/28 23:03:38 print out panic}}()panic(print out panic)} 由于 panic、recover 参数类型为 interface{}因此可抛出任何类型对象。 func panic(v interface{})func recover() interface{} 延迟调用中引发的错误可被后续延迟调用捕获但仅最后一个错误可被捕获:
func test() {defer func() {// defer panic 会打印fmt.Println(recover()) //defer panic}()defer func() {panic(defer panic)}()panic(test panic)
}func main() {test()
}除用 panic 引发中断性错误外还可返回 error 类型错误对象来表示函数调用状态: type error interface {Error() string
} 标准库 errors.New 和 fmt.Errorf 函数用于创建实现 error 接口的错误对象。通过判断错误对象实例来确定具体错误类型。
package mainimport (errorsfmtlog
)var divError errors.New(div error:分母不能为0)func div(x, y int) (int, error) {if y 0 {return 0, divError }return x / y, nil
}func main() {defer func() {if err : recover(); err ! nil {log.Fatalln(err) //2024/02/28 23:17:14 div error:分母不能为0}}()res, err : div(8, 0)switch err {case nil:fmt.Println(res)case divError:panic(err)}}