• Golang的函数(func)


    前言

    Go中对函数的使用非常普遍,Go语言中没有默认参数这个概念。

    函数格式

    func 函数名(参数1,参数2,......)(返回值1,返回值2,....){   }

    package main
    
    import (
    	"fmt"
    )
    
    //函数
    
    //函数的定义:Go是强类型语言必须给 参数、和返回值指定数据类型
    //返回值使用和参数各使用()分开
    func sum(x1 int, x2 int) (ret int) {
    	return x1 + x2
    
    }
    
    //没有返回值的函数
    func f1(x1 int, x2 int) {
    	println(x1 + x2)
    }
    
    //没有参数的函数,也没有返回值的函数
    func f3() {
    	fmt.Println("hello world")
    
    }
    
    //没有参数,但是有返回值的函数
    func f4() string {
    	return "Hello World"
    }
    
    //函数的返回值可以命名也不可不命名
    func f5(x int, y int) (ret int) {
    	ret = x + y
    	return //使用命名返回值,为ret,return后面可省略
    
    }
    
    //返回多个返回值
    func f6() (int, string) {
    	return 1, "2"
    }
    
    //参数的类型简写:当函数有多个参数并且类型一致时,我们可以简写前面的参数类型
    func f7(x, y, z int, n, o string, i, j bool) int {
    	return x + y
    
    }
    
    //可变长的参数:可变长参数必须放在最后
    func f8(x string,y...int){
    	fmt.Println(x)
    	fmt.Println(y) //切片类型[1 2 3]
    }
    
    
    
    func main() {
    	ret := sum(1, 1)
    	fmt.Println(ret)
    	f1(2, 3)
    	str01 := f4()
    	fmt.Println(str01)
    	n, s := f6()
    	fmt.Println(n, s)
    	f8("下雨了",1,2,3)
    
    }
    

    函数参数

    在Go语言中函数参数 通过 值类型传递!也就是说Go函数中的参数都是副本!

    In Go, the function parameters are passed by value.
    With respect to use slice as a function argument, that means the function will get the copies of the slice: a pointer which points to the starting address of the underlying array, accompanied by the length and capacity of the slice.
    Oh boy! Since you know the address of the memory which is used to store the data, you can weak the slice now. Let's see the following example:
     
    Go语言中函数中的参数传递的是值类型
    至于/关于(with respect to...)使用切片作为函数参数,意味着该函数将会得到1个该切片的副本 - 该副本切片指向了该切片的底层数组的起始位置。
    切片中有什么元素由该切片指向的底层数组决定, 2个切片指向了同1个底层数组不同的位置,2个切片中如果有1个修改了这个底层数组同1个位置,会影响另1个切片的元素,如果是追加则不会。
    package main
    
    import "fmt"
    
    var s1 []string
    
    func modify(s1 []string) {
    	s1[0] = "Zero" //修改了s1副本和s1本身对应的底层数组的同1个位置
    }
    
    func expansion(s2 []string) {
    	s2 = append(s2, "3")//扩展了、不算修改底层数组原处!
    	fmt.Println("我append数据了啊:",s2)
    
    }
    
    func main() {
    	s1 = []string{"1", "2"}
    	s1[0] = "一"
    	fmt.Println(s1)
    	modify(s1)
    	fmt.Println(s1)
    	expansion(s1)
    	fmt.Println("增加了没有",s1)
    
    }
    

      

    可变参数

    可变长的参数 

    package main
    
    import "fmt"
    
    func f1(args ...interface{}) {
    	//args空接口类型的slice类型
    	fmt.Printf("类型:%T,可变参数值:%v
    ", args, args)
    }
    
    func main() {
    	f1()
    	f1(1)
    	f1(2, 3, 4)
    	//拆开传值
    	slice1 := []interface{}{5, 6, 7}
    	f1(slice1...)
    }
    

      

     

    闭包函数

    1个函数嵌套了另1个函数,内层的函数引用了外层函数的参数或者变量之后,这个外层函数就行包一样包住了内层的函数,外层函数就叫闭包函数。

    值得注意得是在Go的函数里面,你只能定义1个匿名函数,不能像Python 一样在函数内部在声明1个有名函数。

         Python versus Golang     

    闭包的原理

    1.函数A可以作为函数B的1个参数传到函数B。

    2.函数查找变量的顺序为 首先在自己内部找,找不到再去外层函数找。

    3.函数A也可以作为函数B的返回值返回。

    package main
    
    import "fmt"
    
    //1个简单的闭包函数  结束1个 int参数x,返回1个参数为int和返回值为int的函数
    
    func adder(x int) func(int) int {
    	//内部的匿名函数引用了外部adder函数的变量x就称为闭包
    	return func(y int) int {
    		x += y
    		return x
    	}
    }
    
    func main() {
    	ret := adder(10)
    	fmt.Println(ret(100))
    }
    

      

    package main
    import "fmt"
    
    //1.需求在不改变CEO和CTO写得代码的情况下,把CTO写得的函数传到CEO函数中执行
    
    //CEO写得函数(代码不能动)
    func ceo(f func()) {
    	fmt.Println("This is CEO 函数")
    	f()
    }
    
    //CTO写的函数
    func cto(y...int) {
    	var sum int
    	for _,v:=range(y){
    		sum+=v
    	}
    	fmt.Println("This is CTO 函数",sum)
    }
    
    
    //我写得函数
    func wraper(f func(...int), a ...int) func() {
    	tmp := func() {
    		f(a...)
    	}
    	fmt.Println("This is function code by me")
    	return tmp
    }
    
    func main() {
    	// //匿名函数
    	// var f1 = func(x, y int) {
    	// 	fmt.Println(x + y)
    	// }
    	// f1(10, 20)
    
    	// //立即执行函数
    	// func(x, y int) {
    	// 	fmt.Println(x + y)
    
    	// }(10, 200)
    	//闭包
    	w := wraper(cto, 2020, 4,4,10,41)
    	ceo(w)
    
    }
    

      

      

    闭包面试题

    package main
    
    import "fmt"
    
    func cal(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 := cal(10)
    	//由于 内部函数一直引用外部函数的变量i,所以外部函数的变量i一直在变。
    	fmt.Println(f1(1), f2(2)) //11 9
    	fmt.Println(f1(3), f2(4)) //12 8
    	fmt.Println(f1(5), f2(6)) //13 7
    
    }
    

      

    递归函数 

    递归。递 、 归 所说的就是 递和归2个不同动作过程。有递也有归才叫递归。递就是压栈的过程,归就是出栈的过程。

    递归函数就是自己调用自己

    应用场景:问题相同但是每次问题规模都减小

    必须有1个退出条件:递 、归 。递 、归不能无限得传递(压栈)达到了1临界值(达到了目的) 就得归(出栈)。

     

    package main
    
    import "fmt"
    func test(n int){
    	fmt.Println("--->n=",n)
    	if n<3{
    		n++
    		test(n)//从这里进去的,就从这里出来
    	}
    	fmt.Println("<----n=",n)
    	
    }
    func main() {
    	test(1)
    }
    
    
    /*
    main函数开始执行
    test(1):栈(一)
    	1.fmt.Println("--->n=",n) 打印:--->n= 1
    	2.if n<3{n=1所以条件成立}n++之后 n=2
    	3.遇到函数自己调用自己,开辟新栈(二)也就是test(2)
    ###############################################################	
    	13.出栈二来到在栈一步骤3位置,代码往下执行fmt.Println("<----n=",n)栈一中此时变量n=3啊,所以打印:<----n= 2
    	14.main函数结束
    
    test(2)	栈(二)
    	4.fmt.Println("--->n=",n) 打印:--->n= 2
    	5.if n<3{n=2所以条件成立}n++之后   n=3
    	6.遇到函数自己调用自己,开辟新栈(三)也就是test(3)
    ###############################################################
    	11.出栈三 来到在栈二步骤6的位置,代码往下执行fmt.Println("<----n=",n)栈二中此时变量n=3啊,所以打印:<----n= 3	
    	12.栈(一)步骤3给我压栈的栈,我出栈就到栈(一)步骤3	
    
    test(3)栈(三)
    	7.fmt.Println("--->n=",n) 打印:--->n= 3
    	8.if n<3{n=3所以条件不成立了} n++不执行 test()也不会调用自己进行压栈
    	9.不压栈了就出栈,看到fmt.Println("<----n=",n)就执行所以打印:<----n= 3
    	10.然后出栈,步骤6给我压栈的栈,我出栈了就从步骤6 出去	
    
    
    */
    

      

    文件夹&递归思想

    递归思想对我们影响深远,每天打开电脑进入文件夹、查找子文件、进入子文件 也算得上是递归操作

    :进入1个目录 ------>进入这个目录的子目---------> 进入子目录的子目录......

    临界值:找到/看到自己想要的

    :再逐步退出来

    rm -rf /*  中的 -r 选项是什么意思?

    -r, -R, --recursive remove directories and their contents recursively

    import  os
    file_info=[]
    def recursion_files(inital_path="D:goproject"):
        files = os.listdir(inital_path)
        if not  files:
            return
        for item in files:
            fileAbs = os.path.join(inital_path,item)
            if os.path.isfile(fileAbs):
                file_info.append(fileAbs)
            else:
                #如果有文件夹就把该文件夹下的子目录保存进行压栈操作
                recursion_files(inital_path=fileAbs)
    recursion_files()
    print(file_info)
    

      

    阶乘

    package main
    import "fmt"
    func f(n int) int {
    	if n <= 1 {
    		return 1
    	}
    	return n*f(n-1)
    }
    func main() {
    	ret:=f(5)
    	fmt.Println(ret)
    }
    

      

     有N个台阶,1次可以走1步,也可以走2步,请问有多少种走法?

    package main
    
    import "fmt"
    
    func f(n int) int {
    	if n == 1 {
    		return 1
    	}
    	if n == 2 {
    		return 2
    	}
    
    	return f(n-1) + f(n-2)
    }
    func main() {
    	ret := f(3)
    	fmt.Println(ret)
    }
    

      

    defer关键字

    在Go的函数里可以声明defer关键字,有延迟调用的效果,多用于文件句柄、soket资源释放。

    声明1个defer就是开辟1个独立的栈,在函数体内语句执行完后在按顺序出栈。

    defer的执行步骤如下:

    1.先执行函数中语句内容

    2.遇到defer关键字 开辟独立的defer栈空间(不同于函数)逐一压栈(不执行)

    3.给函数中return值=赋值

    4.按先入后出的顺序 执行defer栈中的语句内容

    5.函数返回真正的返回值(执行ret指令)
    package main
    
    import "fmt"
    
    func f1() int {
    	x := 5
    	defer func() {
    		x++ //
    	}()
    	return x
    }
    
    func f2() (x int) {
    	defer func() {
    		x++
    	}()
    	return 5 //1.先给返回值赋值 x=5 2.defer压栈x=5 3.执行x=5+1 4.return x=6
    }
    
    func f3() (y int) {
    	x := 5
    	defer func() { //压栈时开辟了独立的空间x=5 x+1
    		x++
    	}()
    	return x //这里是x=y=5
    }
    
    func f4() (x int) {
    	defer func(x int) {
    		x++ //参数改写的是副本
    	}(x)
    	return 5 //1.先给返回值赋值x=5 2.defer开辟独立的栈 压栈x=5 3.执行x++ 4.returm
    }
    func main() {
    	fmt.Println(f1()) //5
    	fmt.Println(f2()) //6
    	fmt.Println(f3()) //5
    	fmt.Println(f4()) //5
    }
    

      

     当defer的函数语句中遇到函数先执行再压栈

    package main
    
    import "fmt"
    
    func calc(index string, a, b int) int {
    	ret := a + b
    	fmt.Println(index, a, b, ret)
    	return ret
    }
    
    func main() {
    
    	a := 1
    	b := 2
    	//1.先执行calc("10", a, b)打印"10" 1 3 4
    	//2.压栈1 defer calc("1", a, 4)
    	defer calc("1", a, calc("10", a, b))
    	a=0
    	//3.再执行calc("20", a, b)打印"20" 0 2 2
    	//4.压栈2:defer calc("2", 0, 2)
    	defer calc("2", a, calc("20", a, b))
    	//栈2出栈:2 0 2 2 
    	//栈1出栈:1 1 3 4
    	b=1
    	
    
    }
    

      

    分金币

    你有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’ 函数
     
    package main
    
    import "fmt"
    
    var (
    	coins = 50
    	users = []string{
    		"Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth",
    	}
    	distribution = make(map[string]int, len(users))
    )
    
    /*
    你有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’ 函数
    */
    
    func dispatchCoins() {
    	for _, user := range users {
    		for _, c := range user {
    			switch c {
    			case 'e', 'E':
    				distribution[user]++
    				coins--
    			case 'i', 'I':
    				distribution[user] += 2
    				coins -= 2
    			case 'o', 'O':
    				distribution[user] += 3 //分金币
    				coins -= 3              //分出金币之后需要从总金币数量扣除
    			case 'u', 'U':
    				distribution[user] += 4
    				coins -= 4
    
    			}
    		}
    
    	}
    	fmt.Println(distribution) //打印每人分到的金币
    	fmt.Println("剩余", coins)  //剩余的金币
    }
    func main() {
    	dispatchCoins()
    }
    

      

    参考

  • 相关阅读:
    SQL Server数据库开发基础
    C#面向对象的概念 ----继承,里氏转换和几种集合(2)
    C#面向对象的概念 ----继承,里氏转换和几种集合(1)
    C#面向对象的概念
    C#的引用类型及stringbuilder类(增补)
    C#方法构建的简单介绍
    C#的结构和数组
    C#debug技巧和反编译器
    C#的语法----程序结构(6)
    C#的语法----程序结构(5)
  • 原文地址:https://www.cnblogs.com/sss4/p/12591414.html
Copyright © 2020-2023  润新知