函数
1、函数的基本组成
关键字func,函数名,参数列表,返回值,函数体,返回语句。
2、函数的定义
举个栗子
package mymath import "errors" func Add(a int, b int) (ret int, err error) { if a < 0 || b < 0 { // 假设这个函数只支持两个非负数字的加法 err= errors.New("Should be non-negative numbers!") return } return a + b, nil // 支持多重返回值 }
如果参数列表中若干个相邻的参数类型的相同,比如上面例子中的a和b,则可以在参数列表 中省略前面变量的类型声明,如下所示:
func Add(a, b int)(ret int, err error) { // ... }
如果返回值列表中若干个相邻返回值类型相同,也可以如上面的方式合并
如果只有一个返回值,还可以这样写
func Add(a,b int) int{ //... }
3、函数的调用
函数的调用非常方便,只要事先引入了函数所在的包,就可以直接用下面的方式调用函数
import "mymath"; c := mymath.Add(a,b);
注意:小写字母开头的函数,只在本包内可用,大写字母开头的函数才能被其他包使用,类型和变量也一样。
4、不定参数
a、不定参数类型
不定参数是指传入参数的个数为不定数量,要做到这一点,首先要把函数定义成接受不定参数的类型:
func myfunc (args ...int) int { for _,arg := range args { fmt.Println(arg) }}
调用方式
myfunc(1,2,3) myfunc(1,2,3,4,5)
形如...type格式的类型只能作为函数的参数类型存在,并且必须是最后一个参数。它是一 个语法糖(syntactic sugar),即这种语法对语言的功能并没有影响,但是更方便程序员使用。通 常来说,使用语法糖能够增加程序的可读性,从而减少程序出错的机会。
从内部实现机理上来说,类型...type本质上是一个数组切片,也就是[]type,这也是为 什么上面的参数args可以用for循环来获得每个传入的参数。
b、不定参数的传递
func myfunc(args ...int) { // 按原样传递 myfunc3(args...) // 传递片段,实际上任意的int slice都可以传进去 myfunc3(args[1:]...) }
c、任意类型不定参数
如果希望传递任意类型,可以把参数类型定义为interface{},看看fmt.Println的原型
func Println(format string, ...interface{}){ //... }
5、多返回值
如果调用方调用了一个具有多返回值的方法,但是却不想关心其中的某个返回值,可以简单 地用一个下划线“_”来跳过这个返回值,比如下面的代码表示调用者在读文件的时候不想关心函数返回的错误码:
_,c := read();
6、匿名函数与闭包
匿名函数指不定义函数名的一种函数实现方式。
a、匿名函数
在GO里面,函数可以像普通变量一样传递,跟C的回调函数比较相似,不同的是,GO语言支持在代码里定义匿名函数
func (a,b int ,z float){ return a*b < int(z) }
匿名函数可以直接赋值给一个变量或者直接执行:
f := func (a,b int){ return a+b } func (ch chan char){ return ch <- ACK }(reply_chan)//函数后面直接跟参数列表表示函数调用
b、闭包
匿名函数就是一个闭包。
闭包基本概念:
闭包是可以包含自由(未绑定到特定对象)变量的代码块,这些变量不在这个代码块内或者 任何全局上下文中定义,而是在定义代码块的环境中定义。要执行的代码块(由于自由变量包含 在代码块中,所以这些自由变量以及它们引用的对象没有被释放)为自由变量提供绑定的计算环 境(作用域)。
闭包的价值:
闭包的价值在于可以作为函数对象或者匿名函数,对于类型系统而言,这意味着不仅要表示数据还要表示代码。支持闭包的多数语言都将函数作为第一级对象,就是说这些函数可以存储到变量中作为参数传递给其他函数,最重要的是能够被函数动态创建和返回。
GO语言中的闭包:
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的安全性。