函数对代码进行复用。这和定义一个函数类似,不同的是是对代码块起一个名字,并且可以定义一些参数,这些参数可以进行传递和接收后进行处理 ,处理的结果也需要返回。可见,函数定义有:函数名,函数体,函数参数,返回值。
1.函数
- go没有默认值,go的函数传递是非常简单,安装参数位置进行传递。
其中,参数和返回值是可以有,也可以没有的。
如果代码形式就成了下面这样
func 函数名(函数参数) (返回值) {}
函数参数(行参),是函数快的局部变量。行参返回值都需要描述参数类型和返回值的类型。
- 如果行参配置后,也必须传入相应的值
此前,使用的 fnuc main
也是函数
func main(){}
函数也可以赋值给变量,存储在数组,切片,映射中,也可以作为参数传递给函数或作为函数返回值进行返回。函数的参数,数量,对应类型,以及函数返回值称为函数的签名。
1.1无参数无返回值函数
现在,简单写一个hello world.
package main
import "fmt"
func sayHello(){
fmt.Println("Hello world!")
}
而后在main函数中调用,并打印类型
func main(){
fmt.Printf("%T",sayHello)
sayHello()
}
代码块
package main
import "fmt"
func sayHello(){
fmt.Println("Hello world!")
}
func main(){
fmt.Printf("%T",sayHello)
sayHello()
}
运行
[root@LinuxEA /opt/Golang/work3]# go run func.go
func()Hello world!
Hello world!
在main函数中可以调用多次sayHello函数
package main
import "fmt"
func sayHello(){
fmt.Println("Hello world!")
}
func main(){
fmt.Printf("%T",sayHello)
sayHello()
sayHello()
sayHello()
sayHello()
sayHello()
}
运行
[root@LinuxEA /opt/Golang/work3]# go run func.go
func()Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
1.2有参数无返回值函数
如上,在sayHello中加上参数name string
。这里的参数也可以称为行参
如果设置了多少个行参,也必须传入多个。
func sayHello(name string){
fmt.Printf("Hello %s!",name)
}
name string
是字符串类型,调用的时候也需要传入字符串.
这里传入的是"mark"
给sayHello
,打印的时候打印的name
就是mark
。main函数中传递的参数也可以称为实参
func main(){
sayHello("mark")
}
代码块
[root@LinuxEA /opt/Golang/work3]# cat func2.go
package main
import "fmt"
func sayHello(name string){
fmt.Printf("Hello %s!",name)
}
func main(){
sayHello("mark")
}
运行
[root@LinuxEA /opt/Golang/work3]# go run func2.go
Hello mark!
或者也可以赋值传递
name := "sean"
sayHello(name)
代码块
[root@LinuxEA /opt/Golang/work3]# cat func2.go
package main
import "fmt"
func sayHello(name string){
fmt.Printf("Hello %s!",name)
}
func main(){
sayHello("mark")
fmt.Println("n")
name := "sean"
sayHello(name)
}
运行
[root@LinuxEA /opt/Golang/work3]# go run func2.go
Hello mark!
Hello sean!
1.3有返回值有参数函数
写一个计算加法的函数,如下
func add(a,b int)int{
return a + b
}
有俩个参数,分别是a和b,都是int类型,返回值也是Int类型。直接return行参即可
在main函数中给add传入两个实参。并且需要一个值来接收。比如a。并且打印
func main(){
a := add(1,2)
fmt.Println(a)
}
也可以直接打印
fmt.Println(add(1,2))
代码块
package main
import "fmt"
func add(a,b int)int{
return a + b
}
func main(){
a := add(1,2)
fmt.Println(a)
}
运行
[root@LinuxEA /opt/Golang/work3]# go run add.go
3
1.4可变参数
可变参数也需要定义名称,参数类型使用...
。三个点
- 可变参数只能定义一个,并且一般只能定义在末尾
定义a和b,而后定义可变参数args,可以有,也可以没有,使用三个点表示,而后写上类型,这里使用的是int,最后打印并返回一个0 .如下:
并且打印出args类型:
func addN(a,b int,args ...int) int {
fmt.Println(a,b,args)
fmt.Printf("%T",args)
return 0
}
在main中调用的时候,可以传入args,也可以不传
func main(){
fmt.Println(addN(1,2))
fmt.Println(addN(1,2,3,4,5,6))
}
运行
[root@LinuxEA /opt/Golang/work3]# go run addn.go
1 2 []
[]int0
1 2 [3 4 5 6]
[]int0
可以看到,args的类型是一个切片。
1.5计算可变参数值
如上,现在将可变参数中的值进行相加,可变元素和a,b相加,args是切片。那么,现在通过遍历进行相加。
首先,将a,b相加
titak := a + b
而后遍历args切片,进行相加
for _,v := range args {
titak += v
}
最后返回
return titak
代码块
[root@LinuxEA /opt/Golang/work3]# cat addn.go
package main
import "fmt"
func addN(a,b int,args ...int) int {
titak := a + b
for _,v := range args {
titak += v
}
return titak
}
func main(){
fmt.Println(addN(1,2))
fmt.Println(addN(1,2,3,4,5,6))
}
运行
[root@LinuxEA /opt/Golang/work3]# go run addn.go
3
21
1.6args函数中传递
接上述的函数。现在创建一个cacl的函数,将addN的args传递进来。
args默认是一个空切片,这时候就可以使用args的解包操作args...
。解包的作用是将args从传递中进行解包(在本节案例中,解包后就是int类型),所以这里是有三个点,否则就变成了传递args元素。
...操作只能在切片上
如下:
func cacl(op string,a,b int,args ...int) int {
switch op {
case "add":
return addN(a,b,args...)
}
return -1
}
而后在main函数中传递op字符串参数,a,b的int类型,args是可选参数。
cacl("add",1,2)
:传递给cacl
,第一个参数add,第二个1,1对于a,第二个2,2对应2。第三个参数没有,args为空切片。调用addN函数相加
cacl("add",1,2,5)
:传递给cacl
,第一个参数add,第二个1,1对于a,第二个2,2对应2。第三个参数是5,args解包切片中是5。调用addN函数相加
cacl("add",1,2,5,8)
:传递给cacl
,第一个参数add,第二个1,1对于a,第二个2,2对应2。第三个参数是5,args解包切片中是5。第四个参数是8,args解包切片中是8。调用addN函数相加
如下:
func main(){
fmt.Println(cacl("add",1,2))
fmt.Println(cacl("add",1,2,5))
fmt.Println(cacl("add",1,2,5,8))
}
代码块
[root@LinuxEA /opt/Golang/work3]# cat addn2.go
package main
import "fmt"
func addN(a,b int,args ...int) int {
titak := a + b
for _,v := range args {
titak += v
}
return titak
}
func cacl(op string,a,b int,args ...int) int {
switch op {
case "add":
return addN(a,b,args...)
}
return -1
}
func main(){
fmt.Println(cacl("add",1,2))
fmt.Println(cacl("add",1,2,5))
fmt.Println(cacl("add",1,2,5,8))
}
运行
[root@LinuxEA /opt/Golang/work3]# go run addn2.go
3
8
16
除了以上的方式,在main函数中还可以修改,通过args的方式传入
func main(){ args := []int{1,2,3,4,56,67} fmt.Println(addN(1,2,args...)) fmt.Println(cacl("add",1,2,args...)) }
运行
[root@LinuxEA /opt/Golang/work3]# go run addn2.go 136 136
1.7解包删除切片
如下:
nums := []int{1,2,5,8,0}
删除中间的5
和之前的copy删除类型,用nums[:2]
,拿出索引start-2
,在用nums[3:]
,拿出索引3到END
。
将nums[:2]
,nums[3:]...
打印
fmt.Println("nums[:2]:",nums[:2])
fmt.Println("nums[3:]:",nums[3:])
拿到的结果是
[root@LinuxEA /opt/Golang/work3]# go run del.go
nums[:2]: [1 2]
nums[3:]: [8 0]
加在一起就是1,2,8,0
,而后使用append添加到nums
中。
- 这里必须用到
nums[3:]...
的解包。这是核心
nums = append(nums[:2],nums[3:]...)
代码块
package main
import "fmt"
func main(){
nums := []int{1,2,5,8,0}
nums = append(nums[:2],nums[3:]...)
fmt.Println(nums)
}
运行
[root@LinuxEA /opt/Golang/work3]# go run del.go
[1 2 8 0]
1.8返回值
go中,函数所有的分支必须有返回值。在一个函数的逻辑中需要。在上述代码中return了-1,这里返回-1是因为返回值是int类型。如下:
func cacl(op string,a,b int,args ...int) int {
switch op {
case "add":
return addN(a,b,args...)
}
return -1
}
return -1,-1在这里是说(只要是int类型就可以 ),不管有没有返回的结果,这里都会返回-1。因为在函数逻辑中这里是有返回值的。
这似乎并不明了,写个简单的示例.
执行两次
return 1
return 0
并进行标注,看会发生什么
package main
import "fmt"
func testReturn() int {
fmt.Println("return 1 前")
return 1
fmt.Println("return 1 后")
return 0
}
func main(){
fmt.Println(testReturn())
}
运行
[root@LinuxEA /opt/Golang/work3]# go run return.go
return 1 前
1
这里看到只是执行了return 1
,这是因为代码执行到return后就不会在执行后面的了。而上述定义的return 2
在执行了return 1
后是永远都不会执行的。
- 而在go中支持多个返回值。
多返回值示例:
cacl函数,有a和b两个行参,满足加减乘除。在go中,支持多个返回值,于是,代码就可以写成这样
return a+b,a-b,a*b,a/b
而return了四个返回值后,也需要4个类型来接收
func calc(a,b int) (int,int,int,int) {
return a+b,a-b,a*b,a/b
}
代码块
package main
import "fmt"
func calc(a,b int) (int,int,int,int) {
return a+b,a-b,a*b,a/b
}
func main(){
fmt.Println(calc(9,3))
}
运行计算,a是9,b是3的加减乘除计算结果
[root@LinuxEA /opt/Golang/work3]# go run calc.go
12 6 27 3
当然,在main函数中,这里也可以分开传递,并且可以进行单个屏蔽,比如屏蔽a4,也就是除法,使用下划线即可。
func main(){
a1,a2,a3,_ := calc(9,3)
fmt.Println(a1,a2,a3)
}
运行
[root@LinuxEA /opt/Golang/work3]# go run calc.go
12 6 27
命名返回值
上述的返回值是没有定义名称的,我们也可以定一个名称,定义名称后就可以直接return 返回值变量的最终结果返回。还是以加减乘除,如下:
func calc2(a,b int) (sum int,diff int,product int,merchant int) {
sum = a + b
diff = a - b
product = a * b
merchant = a / b
return
}
func main(){
fmt.Println(calc2(10,2))
}
运行
[root@LinuxEA /opt/Golang/work3]# go run calc2.go
12 8 20 5
如果没有任何赋值就会返回0。
命名返回值在代码量较多的时候查看和使用较为不便。
1.9递归
递归是指函数直接或间接调用自己,递归常用于解决分治问题,为相同的小问题进行解决,需要关注终止条件。
计算0到100的和,用n的阶乘:f(n) 1...n
0 - 100
的和,等于0-99
的和加上100,用函数表示就是f(99)+100
0 - 99
的和,等于0-98
的和加上99,用函数表示就是f(98)+99
函数就成了f(n) = n + f(n-1)
,而f(1) = 1的时候
- 使用递归实现
仍然计算一个1到100的和,假设是Addn的函数,返回值也是int.
return的结果就是n + Addn(n-1)
func Addn(n int) int{
return n + Addn(n-1)
}
计算0-5的和.
func main(){
fmt.Println(Addn(5))
}
这样就相当于从5开始计算
Addn(5) => 5 + Addn(4)
Addn(4) => 4 + Addn(3)
Addn(3) => 3 + Addn(2)
Addn(2) => 2 + Addn(1)
Addn(1) = 1 + Addn(0)
终止条件
这样就会一直执行,递归会一直调用自己,形成了死循环。需要一个结束条件语句。
当n等于1的时候就返回
if n == 1 {
return 1
}
如下:
func Addn(n int) int{
if n == 1 {
return 1
}
return n + Addn(n-1)
}
这样代码执行完成
package main
import "fmt"
func Addn(n int) int{
if n == 1 {
return 1
}
fmt.Println("计算f(n):",n)
return n + Addn(n-1)
}
func main(){
fmt.Println(Addn(5))
}
运行
[root@LinuxEA /opt/Golang/work3]# go run add2.go
计算f(n): 5
计算f(n): 4
计算f(n): 3
计算f(n): 2
15