GO开发[四]:golang函数
函数
1.声明语法:func 函数名 (参数列表) [(返回值列表)] {}
2.golang函数特点:
a. 不支持重载,一个包不能有两个名字一样的函数
b. 函数是一等公民,函数也是一种类型,一个函数可以赋值给变量
c. 匿名函数
d. 多返回值
定义函数类型type:
package main
import "fmt"
type add_func func(int, int) int
func add(a, b int) int {
return a + b
}
func sub(a, b int) int {
return a - b
}
func operator(op add_func, a int, b int) int {
return op(a, b)
}
func main() {
c := add
fmt.Println(c)
sum := operator(c, 100, 200)
fmt.Println(sum)
var x add_func
x=sub
fmt.Println(x)
sub := operator(x, 100, 200)
fmt.Println(sub)
}
3.函数参数传递方式:
1). 值传递
2). 引用传递
注意1:无论是值传递,还是引用传递,传递给函数的都是变量的副本,不过,值传递是值的拷贝。
引用传递是地址的拷贝,一般来说,地址拷贝更为高效。而值拷贝取决于拷贝的对象大小,对象越大,则性能越低。
注意2:map、slice、chan、指针、interface默认以引用的方式传递
package main
import "fmt"
func modify(a *int) {
*a = 100
}
func main() {
a := 8
fmt.Println(a)
modify(&a)
fmt.Println(a)
}
参数类型省略:
func f(i, j, k int, s, t string)
func f(i int, j int, k int, s string, t string)
4.命名返回值的名字:
package main
import "fmt"
func split(sum int) (x, y int) {
x = sum / 10
y = sum % 10
return
}
func main() {
fmt.Println(split(17))
}
_标识符,用来忽略返回值
5.可变参数:
func add(arg …int) int {
}//0个或多个参数
func add(a int, arg …int) int {
}//1个或多个参数
func add(a int, b int, arg …int) int {
}//2个或多个参数
注意:其中arg是一个slice,我们可以通过arg[index]依次访问所有参数,通过len(arg)来判断传递参数的个数
练习:写一个函数add,支持1个或多个int相加,并返回相加结果
练习:写一个函数concat,支持1个或多个string相拼接,并返回结果
defer
1.当函数返回时,执行defer语句。可以用来做资源清理
2.多个defer语句,按先进后出的方式执行,即最后一个defer语句将最先被执行
3.defer语句中的变量,在defer声明时就决定了。
在函数将要返回前执行:
package main
import "fmt"
func print() {
defer func() {
fmt.Println("defer")
}()
fmt.Println("hello")
}
func main() {
print()
}
多个defer语句:
package main
import "fmt"
func main() {
var i int =1
fmt.Println(i)
defer fmt.Println(i)
//不会立即执行,压到栈里面,函数执行结束后,再执行栈里的
defer fmt.Println("a")
i=10
fmt.Println(i)
}
用途:
1.关闭文件句柄
2.锁资源释放
3.数据库连接释放
内置函数
1.close:主要用来关闭channel
2.len:用来求长度,比如string、array、slice、map、channel
3.new:用来分配内存,主要用来分配值类型,比如int、struct。返回的是指针
package main
import "fmt"
func main() {
var i int
fmt.Println(i)
j := new(int)
fmt.Println(j)
fmt.Println(*j)
*j = 100
fmt.Println(*j)
}
4.make:用来分配内存,主要用来分配引用类型,比如chan、map、slice
new和make的区别:
package main
import "fmt"
func test() {
s1 := new([]int)
fmt.Println(s1)
s2 := make([]int, 10)
fmt.Println(s2)
*s1 = make([]int, 5)
(*s1)[0] = 100
s2[0] = 100
fmt.Println(s1)
fmt.Println(s2)
return
}
func main() {
test()
}
5.append:用来追加元素到数组、slice中
package main
import "fmt"
func main() {
var a []int
a = append(a,10,20,30)
a = append(a,a...)
fmt.Println(a)
}
错误处理error
Go语言引入了一个关于错误处理的标准模式,即error接口,该接口的定义如下:
type error interface {
Error() string
}
1.初始化失败,退出
2.重试,比如if err !=nil,重试3次
3.异常上抛
package main
import "fmt"
func print() {
var p *int
fmt.Println(*p)
}
func main() {
defer func() {
err := recover()
fmt.Println(err)
}()
panic("不想执行下去了")
//print()//1.空指针引用错误
//var n int
//fmt.Println(10 / n) //2.除数为0
/*
var i=3
var slice [3]int
fmt.Println(slice[i])//3.下标越界
*/
}
EOF error
recover.go:
Go语言引入了两个内置函数panic()和recover()以报告和处理运行时错误和程序中的错误场景:
当在一个函数执行过程中调用panic()函数时,正常的函数执行流程将立即终止,但函数中之前使用defer关键字延迟执行的语句将正常展开执行,之后该函数将返回到调用函数,并导致逐层向上执行panic流程,直至所属的goroutine中所有正在执行的函数被终止。错误信息将被报告,包括在调用panic()函数时传入的参数,这个过程称为错误处理流程。
recover()函数用于终止错误处理流程。一般情况下, recover()应该在一个使用defer关键字的函数中执行以有效截取错误处理流程。如果没有在发生异常的goroutine中明确调用恢复过程(使用recover关键字) ,会导致该goroutine所属的进程打印异常信息后直接退出。
package main
import (
"fmt"
"time"
)
func test() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
b:=0
a := 1/b
fmt.Println(a)
return
}
func main() {
for {
test()
time.Sleep(time.Second)
}
}
panic.go
package main
import (
"fmt"
"time"
"errors"
)
func initConfig() (err error) {
return errors.New("init config failed")
}
err := initConfig()
if err != nil {
panic(err)
}
return
}
func main() {
for {
test()
time.Sleep(time.Second)
}
}
递归
一个函数调用自己,就叫做递归。
package main
import (
"fmt"
"time"
)
func recusive(n int) {
fmt.Println("hello golang", n)
time.Sleep(time.Second)
if n > 10 {
return
}
recusive(n + 1)
}
func main() {
recusive(0)
}
递归实现阶乘和斐波那契数列:
package main
import (
"fmt"
"time"
)
func factor(n int) int {
if n == 1 {
return 1
}
return factor(n-1) * n
}
func fab(n int) int {
if n <= 1 {
return 1
}
return fab(n-1) + fab(n-2)
}
func main() {
fmt.Println(factor(5))
for i := 0; i < 10; i++ {
fmt.Println(fab(i))
}
}
函数类型
package main
import "fmt"
func print1(){
fmt.Println("print1")
}
func print2() {
fmt.Println("print2")
}
func func1(n int) int {
return n+1
}
func main() {
//变量是个筐,装这个变量类型的数据
//函数也是个筐,容器,装的函数
var f func()
f = print1
f()
f=print2
f()
var flist [3]func(int) int
flist[0]=func1
m:=flist[0](10)
fmt.Println(m)
fmt.Println(flist[0](10))
}
加减乘除:
package main
import (
"fmt"
"os"
"strconv"
)
func add(m, n int) int {
return m + n
}
func sub(m, n int) int {
return m - n
}
func comp(m,n int) int {
return m * n
}
func div(m,n int) int {
return m/n
}
func main() {
funcmap := map[string]func(int, int) int{
"+": add,
"-": sub,
"*":comp,
"/":div,
}
m, _ := strconv.Atoi(os.Args[1])
n, _ := strconv.Atoi(os.Args[3])
f := funcmap[os.Args[2]]
if f != nil {
fmt.Println(f(m, n))
}
}
匿名函数
匿名函数是指不需要定义函数名的一种函数实现方式
在Go里面,函数可以像普通变量一样被传递或使用,这与C语言的回调函数比较类似。不同的是, Go语言支持随时在代码里定义匿名函数。
package main
import (
"fmt"
"strings"
)
func toupper(s string) string {
return strings.Map(func(r rune) rune {
return r - ('a' - 'A')
}, s)
}
func main() {
f := func(x,y int) int {
return x + y
}
fmt.Println(f(1,2))
f1:=func(x,y int) int {
return x + y
}(1,3)
fmt.Println(f1)
fmt.Println(toupper("hello"))
}
闭包
闭包:一个函数和与其相关的引用环境组合而成的实体
package main
import (
"fmt"
)
func addn(n int) func(int) int {
return func(m int) int {
return m+n
}
}
func main() {
f := addn(3)
fmt.Println(f(12))
}
生成器:
package main
import "fmt"
func iter(s []int) func() int {
var i = 0
return func() int {
if i >= len(s) {
return 0
}
n := s[i]
i += 1
return n
}
}
func main() {
f := iter([]int{1, 2, 3})
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())
}