函数的一般结构组成如下所示
func function_name( [parameter list] ) [return_types] {
函数体
}
函数定义解析:
* func:函数由 func 开始声明
* function_name:函数名称,函数名和参数列表一起构成了函数签名。
* parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
* return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
* 函数体:函数定义的代码集合。
比如下面这个返回int类型的函数
/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
/* 声明局部变量 */
var result int
if (num1 > num2) {
result = num1
} else {
result = num2
}
return result
}
函数的多返回值
func main() {
fmt.Println(Add(1,2))
}
func Add(a, b int) (sub int, err error) {
if a < 0 || b < 0 {
err = errors.New("a,b不同为负数")
return 0,err
}
return a+b,nil
}
----output-----
3 <nil>
关于函数的返回值命名
Go语言中,返回值可以像被变量那样使用,在return没有返回值参数的情况下,会直接返回当前的计算值结果
但是一般不在长函数中使用,会影响代码可读性。
func main() {
fmt.Println(sub(1))
}
func sub(num int)(x,y int) { //返回值是x,y
if num >0 {
x = num+2 //这里的x,y就是返回值中的x,y,被当作变量使用
y = num-3
return //会直接返回x,y的计算值结果,与return x,y同效
}
return //会返回int的默认值:0 , 0,与return 0,0同效
}
递归函数
Go 语言支持递归。但我们在使用递归时,开发者需要设置退出条件,否则递归将陷入无限循环中。
语法格式如下:
func recursion() {
recursion() /* 函数调用自身 */
}
func main() {
recursion()
}
计算阶乘
①普通for循环计算15的阶乘
func main() {
var j int = 1
for i:=15;i>0 ;i-- {
j *=i
}
fmt.Println(j)
}
---output---
1307674368000
②使用Go的递归函数
func Factorial(n int)(result int) {
if n > 0 { //跳出递归的条件
result = n * Factorial(n-1) /* 函数调用自身 */
return result
}
return 1
}
func main() {
var i int = 15
fmt.Printf("%d 的阶乘是 %d\n", i, Factorial(i))
}
匿名函数与闭包
闭包就是一个函数“捕获”和它在同一作用域的其他常量或变量。在Go语言中所有的匿名函数都是闭包。闭包的创建方式与普通函数在语法上几乎一致,但是闭包没有名字,通常都是将闭包赋值给一个变量,或者放到一个映射或切片中。
func main() {
c1 := f(0)
c2 := f(0)
fmt.Println(c1() ) // 1
fmt.Println(c1() ) // 2
fmt.Println(c2() ) // 1
}
// 闭包使用方法
func f(i int) func() int {
return func() int {
i++
return i
}
}
分析:c1跟c2引用的是不同的环境,在调用i++时修改的不是同一个i,因此两次的输出都是1。函数f每进入一次,就形成了一个新的环境,对应的闭包中,函数都是同一个函数,环境却是引用不同的环境。
下面这个例子中,每次调用的都是add_func(),因此i会递增
func main() {
add_func := add(1,2)
fmt.Println(add_func())
fmt.Println(add_func())
fmt.Println(add_func())
}
// 闭包使用方法
func add(x1, x2 int) func()(int,int) {
i := 0
return func() (int,int){ //匿名函数
i++
return i,x1+x2
}
}
----------output------------ 只要闭包的还被使用,那么在闭包的变量会一直存在,比如 匿名函数中的i值
1 3
2 3
3 3
我们来看看一个使用闭包匿名函数与普通调用外部函数的区别
func main() { //普通调用外部函数
var j int = 5
a:= add(j) //这里得到一个函数a,i=10,j=5,里面有一个 fmt.Println(i,j)
a() //这里 调用 fmt.Println(i,j),输出 10,5
j*=2 //将j乘以2
a() //这里依旧是以前的函数,i=10,j=5,j没有受影响
a1:= add(j) //这里将重新获得一个函数,此时i=10,j = 10
a1() //输出10,10
}
func add(j int)func() {
var i int = 10
return func() {
fmt.Println(i,j)
}
}
-----output------
10,5
10,5
10,10
上面的 a在创建时引用的环境与a1引用的环境不同,而又因为j是值传递,因此在a创建时引用的环境中调用的是j的副本,这跟原本j的值是否改变无关。创建a1后,会引入新的j值的副本,因此a不会受影响,而a1会受影响
func main() {
var j int = 5
a:= func() (func()){ //使用闭包匿名函数
var i int = 10
return func() {
fmt.Println(i,j)
}
}() //这里加一个括号表示 函数调用
a()
j *= 2
a()
}
-----output------ //这里输出受到了影响
10 5
10 10
这里闭包会捕获j的值,没有值传递的操作,因此在j的值改变后,调用a会受到影响
网友评论