【Go 学习笔记】5 - Go 函数

函数

函数是Go里的核心设计,通过关键字 func 来声明。

func funcName(input1 type1,input2 type2)(output1 type1,output1 type2){
    // 处理逻辑代码部分
    // 返回多个值
    return value1, value2
}

# 说明
关键字 func 用来声明一个函数 funcName
函数可以有一个或是多个参数,每个参数后面带类型,多个参数用 逗号 分隔
函数可以有多个返回值
示例中返回值声明了两个变量,也可以不声明,直接就两个类型也可以。
如果只有一个返回值,而且没有声明返回值变量,也可以省略包括返回值的括号
如果没有返回值,可以省略最后的返回信息
如果有返回值,必须在函数的外层添加 return 语句。
//示例
package main
import "fmt"

// 返回 a b中的最大值
func max(a,b int) int{
    if a > b {
        return a    
    }
    return b
}

func main(){
    x := 3
    y := 4
    z := 5
    max_xy := max(x, y)
    max_xz := max(x, z)
    fmt.Printf("max(%d,%d) = %d\n",x,y,max_xy)
    fmt.Printf("max(%d,%d) = %d\n",x,z,max_xz)
    fmt.Printf("max(%d,%d) = %d\n",x,y,max(x,y)) //也可以在这里调用函数
}

# 说明
max函数有两个参数,类型都是int 第一个参数的类型可以省略,默认为离他最近的类型。
同时,注意到返回值就一个类型,这个就是省略写法

多返回值

Go 支持多返回值

package main
import "fmt"

func SumAndProduct(a,b int)(int,int){
    // 多返回值,用 逗号 分隔
    return a+b, a*b
}
func main(){
    x := 3
    y := 4
    
    xPlusy, xTimesy := SumAndProduct(x,y)
    fmt.Printf("%d + %d = %d\n",x,y,xPlusy)
    fmt.Printf("%d * %d = %d\n",x,y,xTimesy)
}

# 注意
示例中的返回值,没有声明变量名,只用了两个类型。
如果你的函数是导出的(首字母大写),最后要命名返回值。否则会造成文档的可读性差

变参

变参,是指函数有不确定数量的参数。Go支持变参。
func myfunc (args ...int){}

args ...int  是告诉 go 这个函数接受不定数量的参数。
注意,这些参数的类型都是int。
在函数体重,变量 args 是一个int的slice。
如:
func myfunc(args ...int){
    for _, n := range arg{
        fmt.Printf("And the Number is: %d\n",n)
    }
}

形如 ...type 格式的类型,只能作为函数的参数类型存在,而且必须是最后一个参数。

不定参数的传递

func myfunc(args ...int){
    // 按原样传递
    myfunc3(args ...)
    
    // 传递片段,实际上任意的int slice 都可以传递进去
    myfunc3(args[1:]...)
}

任意类型的不定参数

类型: interface()
语法: func funcName(args ...interface())
如:fmt.Printf的函数
func Printf(format string, args ...interface()){
    //……
}
// 示例
package main
import "fmt"

func myPrint(args ...interface()){
    for _, arg := range args{
        switch arg.(type){
            case int:
                fmt.Println(arg," is an int value")
            case string:
                fmt.Println(arg," is a string value")
            case int64:
                fmt.Println(arg," is an int64 value")
            default:
                fmt.Println(arg," is an unknown tpye")                                                    
        }    
    }
}
func main(){
    var v1 int = 1
    var v2 int64 = 234
    var v3 string = "hello"
    var v4 float32 = 1.234
    myPrint(v1,v2,v3,v4)
}

匿名函数

不需要定义函数名的一种函数。在Go里,可以像普通变量一样被传递和使用。Go 支持随时在代码里定义匿名函数。

# 语法    
func (a,b int,z float64)bool{
    return a*b < int(z)
}

匿名函数可以直接赋值给一个变量或是直接执行

f := func(x, y int) int{
    return x, y
}

func(ch chan int){
    ch <- ACK
}(reply_chan)
// 花括号后直接跟参数列表,表示函数调用。

闭包

Go 的匿名函数,其实就是一种闭包
闭包hi可以包含自由变量(为绑定到特定对象)的代码,这些变量不在这个代码块内或是任何全局上下文中定义,而是在定义代码块的环境中定义。
要执行的代码块为自由变量提供绑定的计算环境。
价值
可以作为函数对象或是匿名函数。对于类型系统而言,这意味着不仅要表示数据,还要表示代码。
这些函数可以存储到变量中作为参数传递。最重要的时候,能够被函数动态创建和返回。
Go 语言中的闭包同样也会引用到函数外使用的变量,闭包的实现确保只要闭包还在使用,那么被闭包引用的变量就会一直存在。

// 示例
package main
import "fmt"

func main(){
    var j int = 5
	a := func()(func()){
	    var i int = 10
		return func(){
		    fmt.Printf("i, j: %d, %d\n", i, j)
		}
	}()
	
	a()
	j *= 2
	a()
}

#  输出结果
i,j : 10, 5
i,j : 10,10

# 说明
变量 a 指向的闭包函数引用了局部变量 i,j。
i 的值被隔离,在闭包外不能被修改。改变 j 的值后,再次调用 a 发现结果是修改过的。
在变量 a 指向的闭包函数中,只有内部的匿名函数才可以访问 i ,无法通过其他方式访问,保证了i 的安全性。

错误处理

Go 语言引入了一个处理错误的标准模式,即error接口,定义如下

type error interface{
    Error() string
}

大多数函数都有返回值,如果返回错误,一般将error作为多种返回值的最后一个。

# 具体使用方法
type PathError struct{
    Op string
	Path string
	Err error
}
//这样定义了后,如何知道PathError作为error来传递呢,用下面的实现方法。
func (e *PathError) Error() string{
    return e.Op + "" + e.Path + ":" + e.Err.Error()
}

//如下面的代码,当syscall.Stat()失败返回err时,将err包装到一个PathError对象中返回。
func Stat(name string)(fi FileInfo, err error){
    var stat syscall.Stat_t
	err = syscall.Stat(name, &stat)
	
	if err != nil{
	    return nil, &PathError("stat", name, err)
	}
	
	return fileinfoFromStat(&stat, name), nil
}

// 如果不满足只打印一行错误信息,可以用类型转换来处理
fi, err := os.Stat("a.txt")
if err != nil{
    if e, ok := err.(*os.PathError); ok && e.Err != nil
	    //获取PathError类型变量 e 中的其他信息并处理
	}
}

defer

defer语句是Go中一个非常有用的特性,可以将一个方法延迟到包裹该方法的方法返回时执行,也可以用来处理关闭文件句柄等收尾操作
Go官方文档中对defer的执行时机做了阐述,分别是。
包裹defer的函数返回时
包裹defer的函数执行到末尾时
所在的goroutine发生panic时
一个函数中可以存在多个defer,调用顺序是先进后出的原则。LIFO
一些特殊的注意点,可以查看: Go语言中defer的一些坑

panic()

内置函数 panic() recover() 以报告和处理运行时错误和程序中的错误场景

func panic(interface())
func recover() interface()

当一个函数执行过程中调用 panic() 函数时,正常的函数执行将立即终止,但函数中之前使用defer延迟执行的语句将正常执行。
错误信息报告将被报告,包括在调用panic() 函数时传入的参数,这个过程称为错误处理流程。
panic() 的参数类型是interface(),这个函数接收任意类型数据。

recover()

recover()用于终止错误处理流程。
一般情况下,recover()应该在一个使用defer的函数中执行以有效截取错误处理流程。

//示例
# 如果我们认为tt()函数执行有问题的话,可以用下面的方式在调用代码中截取recover()
defer func(){
    if r := recover(); r != nil{
	    log.Printf("Runtime error caught: %v", r)
	}
}()
tt()

# 说明
无论 tt() 是否触发错误处理,匿名函数defer都会在函数退出的时候执行。
如果 tt() 触发了错误流程,recover()将使得该错误处理流程终止。
如果处理流程被触发,程序传给panic函数的参数不是nil,则函数还是会打印详细的错误信息
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值