函数

函数部分参见原文:https://www.liwenzhou.com/posts/Go/09_function/

func intSum(x, y int) int { //函数有两个参数,如果这俩参数类型相同,那么可以写到一起,返回值可以取名也可以匿名,但是需要指定范湖类型,
	//并且上面传入的参数叫做形参
	return x + y //函数返回值,需要跟上面的返回类型相同,数量也应该相同.
}

func sayHello() {
	fmt.Println("hello 沙河")
}

func main() {
	intSum(1, 2) //函数调用
	sayHello()   //函数调用

输出结果
C:\Users\34826\AppData\Local\Temp\___go_build_main_go.exe #gosetup
3
hello 沙河

函数中可变参数用法

//可变参数
func intSum2(x ...int) int { //表示函数intSum2的传入参数可以是多个int类型的值.可以理解成一个切片,但是在实参传入时应该是多个int值
	fmt.Println(x)
	v := 0                //定义一个返回用的接收值,初始化等于0
	for _, i := range x { //循环切片x,取出切片中的数值
		v = i + v //计算切片中数值的和
	}
	fmt.Println("intsym2输出结果为", v)
	return v //返回结果

}

//固定参数搭配可变参数使用
func intSum3(x int, y ...int) int { //固定参数x搭配可变参数y进行使用,也可以用在字符串的拼接
	fmt.Println(x, y)
	s := 0
	for _, i := range y {
		s = s + i

	}
	fmt.Println("intsum3结果为:", s+x)
	return s + x
}

func main() {
	intSum2(2)
	intSum2(8, 3, 5)
	intSum2(1, 2, 3, 4, 5, 6, 7, 8) //这里传入的是多个int值,不是直接穿切片
	intSum3(3, 4, 3, 5)             //这里传入的是x和y两个值

}

输出结果:
C:\Users\34826\AppData\Local\Temp\___go_build_main_go.exe #gosetup
[2]
intsym2输出结果为 2
[8 3 5]
intsym2输出结果为 16
[1 2 3 4 5 6 7 8]
intsym2输出结果为 36
3 [4 3 5]
intsum3结果为: 15

返回值

//返回值
//go语言支持多返回值,如果函数有多个返回值需要用()将所有的返回值和类型都抱起来.
func calc(x, y int) (int, int) {
	sum := x + y
	sub := x - y
	return sum, sub
}

//返回值命名
func calc2(x, y int) (sum, sub int) { //这里可以提前定义好返回接受者的名称和类型.接收者需要在函数中得到相应的结果,返回后进行赋值
	sum = x + y
	sub = x - y
	return //这里可以直接返回,因为函数中的返回值已经在定义接受值的时候定义过,所以不用在进行指定的返回.默认返回就可以
}

func main() {
	fmt.Println(calc(5, 7))
	fmt.Println(calc2(6, 4))

}

输出结果:
C:\Users\34826\AppData\Local\Temp\___go_build_code_oldboy_com_studygolang_05lesson5_lesson1.exe #gosetup
12 -2
10 2

全局变量

//全局变量

var num int64 = 10

func testGlobalVar() {
	fmt.Printf("num=%d\n", num) //函数中可以访问全局变量num.
}
func main() {
	testGlobalVar()

}
输出结果:
C:\Users\34826\AppData\Local\Temp\___go_build_main_go.exe #gosetup
num=10

局部变量

func testLocalVar() {
	var x int64 = 10
	fmt.Println("x = %d\n", x)
}

func main() {
	testLocalVar()
	fmt.Println(x) //此时无法访问变量x

}
输出结果:
# code.oldboy.com/studygolang/05lesson5/lesson1
.\main.go:631:14: undefined: x

报错信息是找不到x 变量

//局部变量和全局变量如果重名,优先访问局部变量,然后访问全局变量


var num int64 = 10

func testNum() {
	num := 100
	fmt.Println(num)
}
func main() {
	testNum()

}
输出结果:
C:\Users\34826\AppData\Local\Temp\___go_build_main_go.exe #gosetup
100

语句块中的变量作用范围

//语句块中的定义变量的方式
func testLocalVar(x, y int) {
	fmt.Println(x, y)
	if x > 0 {
		z := 100
		fmt.Println(z)
	}
	//fmt.Println(z)  //这里是无法找到变量z的,因为变量z在条件判断语句中,与当前语句不是统一级别和语义中.所以无法找到z
}
func main() {
	testLocalVar(3, 5)

}
输出结果:
C:\Users\34826\AppData\Local\Temp\___go_build_main_go.exe #gosetup
3 5
100

for语句块中变量的作用域

//for语句块中变量的作用域
func testLocalVar(x, y int) {
	fmt.Println(x, y)
	for {
		fmt.Println(x)
		z := 100
		fmt.Println(y)
		fmt.Println(z)
		break
	}
	//fmt.Println(z) //同样在for循环外无法找到函数.
}

func main() {
	testLocalVar(3, 5)
}
输出结果:
C:\Users\34826\AppData\Local\Temp\___go_build_main_go.exe #gosetup
3 5
3
5
100

函数类型

//定义一个函数类型作为基础函数类型,为后面的相同类型函数进行赋值
type calculation func(int, int) int //定义一个基准函数

func add(x, y int) int {
	return x + y

}
func sub(x, y int) int {

	return x - y
}

func main() {
	//var c calculation //表示将calculation类型赋值给c变量
	c := add //把add赋值给c
	fmt.Printf("type of c:%T\n", c) //打印c的类型
	fmt.Println(c(1, 2)) //调用函数c并打印结果.,这里的过程可以直接理解成func c(x,y int) int{ return x +y} 调用并打印结果
	f := add //同上将f声明一个add类型,并赋值,相同类型,但是不同名称的函数.
	fmt.Printf("type of %T\n", f)
	fmt.Println(f(10, 20))
}

输出结果:
C:\Users\34826\AppData\Local\Temp\___go_build_code_oldboy_com_studygolang_05lesson5_lesson1.exe #gosetup
type of c:func(int, int) int
3
type of func(int, int) int
30

解释一下:
    这里定义了多个函数.add,sub 等,但是如果想给一个新的变量定义成函数相同的类型的话直接使用:=即可 例如 a := add表示将add函数类型赋值给a 并且函数名称也由add变成a,其余操作和执行过程不变.可以理解成快速的创建一个新的函数.

函数作为参数传入到函数中

//首先定义一个函数
func add(x, y int) int {
	return x + y
}

func str(x, y string) string {
	ret1 := fmt.Sprintf(x, y)
	return ret1
}
func calc(x, y int, op func(x, y int) int) int { //这里
	return op(x, y)
}

func main() {
	ret2 := calc(10, 20, add) //将10,20 和add函数传入到calc函数中, 函数calc的执行过程中只有x,y两个参数实体参数.
	//所以calc函数返回一个 op类型函数的执行结果,这里的op函数类型是add函数.add函数计算过程是传入两个参数(已经在calc中传入,x,y int类型)并且add返回x+y,
	//就是将10,20,传入add中然后计算结果,这里的10,20 就是add函数和calc函数中的x,y.所以最后结果是10 + 20 = 30
	fmt.Println(ret2)
	//ret3 := calc(10,20,str) //这里是无法将str传入函数当中的,因为函数str的参数类型与calc指定的参数类型不相符,
	// (只有参数数量,参数类型,返回值类型都相符才可以传入到参数中)否则无法传入到函数的参数中.

}
输出结果:
C:\Users\34826\AppData\Local\Temp\___go_build_main_go.exe #gosetup
30

解释一下.
1.函数作为参数传递到函数中需要当前函数的参数类型与已经定义好的函数的类型一致,否则无法将函数作为参数传递进新的函数做运算.
2.函数传递的参数要预先定义好.或者在现有传入参数的函数中直接定义传入参数的方法.

匿名函数

//函数可以作为返回值,但是在go语言中函数内部不能再像之前那样定义函数,只能定义匿名函数,匿名函数就是没有名字的函数.
func main() {
	add := func(x, y int) {  //将一个匿名函数赋值给add,这样add()就表示执行该函数,只要在括号中传参即可完整执行,得到结果.
		fmt.Println(x + y)
	}
	add(10, 20)
	func(x, y int) {  //创建匿名函数,并指定形参.
		fmt.Println(x + y)
	}(10, 20) //这里在函数最末尾添加一个括号,表示直接执行,并将括号中的实参传入到匿名函数中.

}

输出结果:
C:\Users\34826\AppData\Local\Temp\___go_build_code_oldboy_com_studygolang_05lesson5_lesson1.exe #gosetup
30
30

闭包(有点点难理解)

//闭包
//闭包值得是一个函数与其相关的引用环境组合而成的实体.简单来说, 闭包= 函数+引用环境.

func adder() func(int) int {
	var x int
	return func(y int) int {
		x += y
		return x
	}

}

func main() {
	f := adder()       //表示将函数adder的执行结果赋值给f.这里函数adder的执行结果返回的是一个匿名函数,所以f 应该是func(int)int{}
	fmt.Println(f(10)) //这里的10 是func(y int) int { x += y return x }的参数.也就是y = 10 .
	//同理
	fmt.Println(f(20)) //都表示y = 20 +10
	fmt.Println(f(30)) //y=30 +20 +10
	//执行结果就是因为x没有定义值是多少,所以默认是0.f()函数最终的运算结果是 x=y +0 , 返回值还是y的值.
	f1 := adder()
	fmt.Println(f1(40)) //同上 40
	fmt.Println(f1(50)) //同上 40 +50
}
输出结果:
C:\Users\34826\AppData\Local\Temp\___go_build_main_go.exe #gosetup
10
30
60
40
90


说明一下:
这里发现每次调用f()函数都会累积之前已经定义过的返回值结果.因为我们如果没有将包体内的返回值清零,那么这个返回值就会一直存在.并且累积.函数如果找不到自己环境中的变量就会往更上层进行查找.这里x就是在匿名函数的更外一层中的定义.

闭包进阶1

//闭包进阶1
func adder2(x int) func(int) int {
	return func(y int) int {
		x += y
		return x
	}

}

func main() {
	f := adder2(10)    //这里直接将x的初始值定义为 int类型 值等于10
	fmt.Println(f(10)) //表示adder2函数的匿名函数的参数是10 根据源adder2函数的运算过程,最终结果为20
	fmt.Println(f(20)) //同上 20+20 40
	fmt.Println(f(30)) //同上 30+40 70

	f1 := adder2(20)
	fmt.Println(f1(20)) //20+20 40
	fmt.Println(f1(50)) //40+50 90
}
输出结果
C:\Users\34826\AppData\Local\Temp\___go_build_main_go.exe #gosetup
20
40
70
40
90

闭包进阶2

//闭包进阶2
func makeSuffixFunc(suffix string) func(string) string { //写一个函数makesuffixFunc,并返回一个函数,
	return func(name string) string {
		if !strings.HasSuffix(name, suffix) { //函数的作用是如果参数不包含suffix参数那么就做拼接操作.
			return name + suffix //返回拼接结果
		}
		return name //如果包含指定文件结尾的信息,那么就直接返回name
	}

}

func main() {
	jpgFunc := makeSuffixFunc(".jpg")  //将函数返回值(包内的函数)返回给jpgFunc变量
	txtFunc := makeSuffixFunc(".txt")  //将函数返回值(包内的函数)赋值给txtFunc变量
	fmt.Println(jpgFunc("test")) //test.jpg 将制定名称的参数传入到函数中.并打印结果.
	fmt.Println(txtFunc("test")) //test.txt  同上
}

输出结果:
C:\Users\34826\AppData\Local\Temp\___go_build_main_go.exe #gosetup
test.jpg
test.txt

闭包进阶3

//闭包进阶3

func calc(base int) (func(int) int, func(int) int) {
	add := func(i int) int {
		base += i
		return base
	}
	sub := func(i int) int {
		base -= i
		return base
	}
	return add, sub
}

func main() {
	f1, f2 := calc(10)        //因为将10 传递进了calc函数中,那么10 在函数中的状态会一同保存到f1,f2两个函数中.那么在进行下面的运算的过程中,数值是一直岁调用过程进行变化的.
	fmt.Println(f1(1), f2(2)) //11 9 因为f1 f2 是独立存在的.但是在每次调用的时候base的值都是随着调用改变的.所以就好比下一次调用就必须参考上次调用的结果才行.
	fmt.Println(f1(3), f2(4)) //同上
	fmt.Println(f1(5), f2(6)) //同上
}

输出结果:
C:\Users\34826\AppData\Local\Temp\___go_build_code_oldboy_com_studygolang_05lesson5_lesson1.exe #gosetup
11 9
12 8
13 7

defer的用法

//defer 语句defer执行的时机在返回值之后在ret指令之前  执行return指令 ==> 返回值x ==> defer ==> ret指令
func f1() int {
	x := 5
	defer func() {
		x++
	}()
	return x
}
func f2() (x int) {
	defer func() {
		x++
	}()
	return 5
}
func f3() (y int) {
	x := 5
	defer func() {
		x++
	}()
	return x
}
func f4() (x int) {
	defer func(x int) {
		x++
	}(x)
	return 5
}
func main() {
	fmt.Println(f1()) //5
	fmt.Println(f2()) //6
	fmt.Println(f3()) //5
	fmt.Println(f4()) //5
}

输出结果
C:\Users\34826\AppData\Local\Temp\___go_build_main_go.exe #gosetup
5
6
5
5
不太懂为啥会是这样的结果....

defer烧脑题

func calc(index string, a, b int) int {
	ret := a + b
	fmt.Println(index, a, b, ret) //
	return ret
}

func main() {
	x := 1
	y := 2
	defer calc("AA", x, calc("A", x, y))
	x = 10
	defer calc("BB", x, calc("B", x, y))
	y = 20
}

输出结果:
C:\Users\34826\AppData\Local\Temp\___go_build_main_go.exe #gosetup
A 1 2 3
B 10 2 12
BB 10 12 22
AA 1 3 4

//总结:
//在使用defer函数的时候如果defer函数内部还有其他函数需要根据main函数的从上到下的顺序进行依次执行,
//比如a := calc("AA", x, calc("A", x, y)) 
//b:= calc("A", x, y)
//那么需要优先执行先出发的defer函数内部的其他函数,这里的b函数.执行过程与当前main执行过程有关,即当前x :=1 y :=2 那么b函数最终结果是 打印"A",1,2,3
//同理下面还有一个defer 函数,需要根据x的新值进行函数的计算, 此时 新b的值是 x :=10 y := 2 ret :=12 那么最终结果是 新b函数的输出是  "B" 10 ,2,12
//接下来是defer的特性问题,如果有多个defer函数的话那么需要先进后出,后进先出(堆栈类似)
//那么堆栈上面的defer执行结果为 "BB" 10 ,12 22
//最后执行堆栈最初进入的函数, 就是上面的第一个defer 此时的状态是 AA 1 ,3 ,4 //太特么绕了!!!

作业 分硬币游戏

/*
你有50枚金币,需要分配给以下几个人:Matthew,Sarah,Augustus,Heidi,Emilie,Peter,Giana,Adriano,Aaron,Elizabeth。
分配规则如下:
a. 名字中每包含1个'e'或'E'分1枚金币
b. 名字中每包含1个'i'或'I'分2枚金币
c. 名字中每包含1个'o'或'O'分3枚金币
d: 名字中每包含1个'u'或'U'分4枚金币
写一个程序,计算每个用户分到多少金币,以及最后剩余多少金币?
程序结构如下,请实现 ‘dispatchCoin’ 函数
*/
var (
	coins = 50 //定义硬币的原始数量为 50
	users = []string{
		"Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth",
	}  //定义一个人名的列表
	distribution = make(map[string]int, len(users))  //初始化一个map.用于存放名字和硬币数量.
)

func main() {
	left := dispatchCoin()
	fmt.Println("剩下:", left)
	for i, v := range distribution {  //遍历一个map,将已经填写好的键值都遍历出来并打印结果
		fmt.Println(i, v)
	}

}

func dispatchCoin() (left int) {  //这里还是不太明白为啥需要返回一个left
	for _, name := range users {  //遍历人名,后面通过人名当做键写入已经创建好的map中.
		getcoin := 0
		for _, letter := range name {  //遍历人名中的字母,并将对应出现的字母形式做map值的增加.并且每次增加后要从原始的50枚金币中减少相应的数量.
			if letter == 'E' || letter == 'e' {
				getcoin += 1
				coins -= 1
			} else if letter == 'i' || letter == 'I' {
				getcoin += 2
				coins -= 2
			} else if letter == 'o' || letter == 'O' {
				getcoin += 3
				coins -= 3
			} else if letter == 'u' || letter == 'U' {
				getcoin += 4
				coins -= 4
			} else {
				coins -= 0
				getcoin -= 0
			}

		}
		distribution[name] = getcoin //这里表示每个键值对对应的值.也表示每个人命分得的钱数
	}
	left = coins  //因为变量中已经定义了返回值是int类型,所以这里只要将left赋值后返回即可
	return //默认返回的是left变量.因为需要取到剩余值?然后代码中因为已经指定了left所以直接返回即可
}

输出结果:  因为map的遍历结果是无序的.所以以下的返回结果每次执行顺序是不同的.但是结果是相同的.
C:\Users\34826\AppData\Local\Temp\___go_build_code_oldboy_com_studygolang_05lesson5_lesson1.exe #gosetup
剩下: 10
Sarah 0
Peter 2
Adriano 5
Emilie 6
Giana 2
Aaron 3
Elizabeth 4
Matthew 1
Augustus 12
Heidi 5


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值