Go语言学习笔记04--特殊函数&工程化结构&数组&随机数

1.匿名函数
    go语言中的函数都是声明在函数之外的,并不存在函数内声明函数的问题
    但是也会存在一些特殊情况,在这写情况中允许在函数内部去再次定义一个函数。
    这种情况下,在函数内部定义的函数就必须遵守一些go语言定义的特殊规则。
    而这些内部的函数,被统称为:匿名函数。
        func main (){
            func (..){..}
        }
    (1)对于go语言中的匿名函数而言,由于其不存在函数名,无法使用传统函数的调用功能
        所以如何调用匿名函数就必须从两个角度出发来解决问题。
            
            1)利用函数指针,完成匿名函数的"重命名",然后再次调用
                func main(){
                    fpointer := func (num int)int{ 
                        return num+10;
                    }
                    fmt.Println(fpointer(100));
                }
            利用函数指针的情况时需要注意,由于函数指针是在函数内部被定义的
            所以函数指针其实是一个局部变量,因此函数指针是只能够在当前所在的函数内使用的
            一旦超出当前所在函数范围,即宣告函数指针失效。

            2)利用AIIFE,即Anonymous-Immediately-Invoked-Function-Expression
              匿名立即自动执行的函数表达式,来完成匿名函数的调用
                  func main(){
                      func (num int){
                          fmt.Println(num+100);
                      }(35);
                  }
            这种方式虽然能够在“真正匿名”的情况下完成函数的调用,但是缺陷也相当明显。
            那就是AIIFE只能在它声明的时候被立即调用一次,无法延迟调用,也无法多次重复调用。

    (2)闭包
        闭包是go语言中匿名函数的另外一种表现形式。
        从概念上来说,闭包是一种:
        使得函数外部可以间接访问函数内部定义的局部变量的手段,在go语言中经常表现为匿名函数的样子。
            func createClousre() func()int{
                var num int = 100;
                //在这里闭包就是 返回的这个匿名函数
                return func ()int{
                    return num;
                }
            }
        闭包在go语言中主要有两个作用:
            
            1)能够使得在函数外部访问到函数内部的局部变量,打破了“局部视障”
                func main(){
                    clo := createClousre();
                    fmt.Println(clo());//输出局部变量num的值 100
                }
                原本在createClousre函数中定义的局部变量num,在main函数中是不可能直接访问到的
                但是我们借助于闭包clo,完成了一次打破“局部视障”的操作

            2)能够将局部变量的生命周期延长,具体延长时间视闭包存在的函数而定
                func main(){
                    clo := createClousre();//createClousre函数在此执行结束
                    fmt.Println(clo());    
                }
                原本createClousre函数在调用完成后就会将其占有的内存销毁,
                即局部变量num也会跟随消失不见,再也无法访问。
                但是由于闭包中持有了局部变量num,于是闭包就将局部变量num的声明周期延长到了clo上
                所以变量clo只要还继续存在,那么局部变量num就得以继续被访问。
2.递归函数
    go语言中的递归函数与传统c语言语法类似,只不过考虑到在go语言中很多运算符是不能直接与调用语句结合。
    故而写法上可能存在一点点差异。
    (1)递归函数:
        递归函数是指在函数的内部,再次调用本身的函数。
        eg:
            func f(){
                ...
                f();
            }
        乍一看这样的写法似乎是一个错误的写法,会导致回调地狱的产生。
        实际上递归只是结构上需要满足这样,而事实中递归函数的构成还需要三个要素。
    (2)递归三要素:
        递归变量赋初值、递归结束条件、递归变量向着递归结束的趋势发生变化
        1)递归变量赋初值
            指的是递归函数总是要有一个“标识”来提供作为判断递归结束的标准
            并且这个“标识”是需要有一个初始值的。
            eg:
                func f(num int){
                    ...
                    f(num);
                }    
            此时形参num就可以充当递归函数的递归变量。
        2)递归结束条件
            指的是递归函数总是要有一个通过标识来进行的判断。
            这个判断用于决定递归何时结束, 毕竟大家都不希望回调地狱的发生
            eg:
                func f(num int){
                    if num <= 1{
                        return num;
                    }
                    f(num);
                }
            此时针对递归变量的条件判断if就成为了递归结束条件,
            只要一旦递归变量满足结束条件,那么递归函数就会立即结束。避免了回调地狱的发生。
        3)递归变量向着递归结束的趋势发生变化
            这一点非常关键,因为递归结束条件之所以能够触发,
            是因为递归变量必须一直在发生变化,而变化就会有趋势
            在递归函数中由于函数最终必须要执行结束,因此递归变量就必须向着递归结束的趋势发生变化
            eg:
                func f(num int){
                    if num <= 1{
                        return num;
                    }
                    num --;
                    f(num);
                }
            这样一来,每次递归调用f()函数的时候,num都是在上一次调用的基础上减少了1
            那么总有一个时刻num的值会满足递归结束条件。
    (3)递归案例-阶乘问题
        func getJieChengSum(num int) int{
            if num<=1{
                return num;
            }
            tempNum := num;
            num--;
            return tempNum*getJieChengSum(num);
        }
        func main() {
            sum := getJieChengSum(10);
            fmt.Println(sum);
        }

3.工程管理
    工程管理指的是goland在编译过程中,一个模块化思想的体现。
    主要变现为:
        1)在一个文件中,可以通过“导入包”的操作后,访问其他文件中的函数。
        2)整个工程分为三类文件夹:src(代码源文件)、pkg(编译生成文件)、bin(系统资源文件)

    (1)“包” 
    包,即package。是go语言中为文件分类,而后在编译文件的过程中对文件合并时的一个名词。
    包中存放着不同模块,每个模块有着自己独立的功能。
    eg:
        package user
            -userLogin.go
            -userRegist.go
            ..
    此时如果加载了user package这个包,就相当于引入了包中所有模块的功能。
    而后就可以通过user包名,来访问包中模块所提供的功能。
    eg:
        user.Login();//假定Login是userLogin.go文件中声明的方法
        user.Regist()//假定Regist是userRegist.go文件中声明的方法

    (2)包与文件夹
    包并不是文件夹,但是通常包名和包文件所在的文件夹设置为相同名字,以便于理解和查看。
    一般上来讲包可以认为是编译完成后在pkg文件夹下的.a文件

    (3)导包
    导入包的目的其实就相当于JS中的link文件或者script引入文件。其目的都只有一个
    那就是在导包后,能够使用包中所提供的不同功能,将工程模块化与组件化。
    eg:    
        import "包路径"
        包名.包中提供的接口方法
    eg:
        src
            -test//包名
                -testFile.go//文件名
                    package test
                    func TTest(){...}//方法名
            main.go
                package main
                import "test"//导入时候,使用的是包名
                func main(){
                    test.TTest();//调用的时候,也是使用的包名
                }
    案例中能够看到在导包结束后,调用包中提供的方法时,使用的是包名而不是文件名!!

    (4)注意事项(重点)
    由于初学go语言,的确在导入自定义包的这个问题上碰了个大坑。
    我使用的是goLang IDE。在导入系统包的时候并不会出现什么问题,主要集中在导入自定义包的时候!
    问题:
        import 无法检索到自定义包,但是手打却会出现包中方法的系统提示。(虽然运行不了)
    缘由:
        1)核心到爆炸的问题:
            goLang这个坑货IDE在加载包的时候不会主动查找当前路径下的文件,即系统环境变量不能自动配置!
            必须采用手动配置的方式,将$GOPATH配置完毕才可以!
            eg:
                file -> preferences for New Projects And Settings -> GO -> GOPATH -> + -> 工程目录
            注意一点,不要添加src路径进去,而是直接添加目录路径即可!
            eg:
                /Users/xxx/Desktop/FuncAndArray
            其中FuncAndArray就是我的工程文件夹名称,也就是说直接将工程路径丢在这就可以了!!!!!
         2)次要问题
             Go语言要求自定义的包中,所提供的模块方法首字母必须大写,否则检索不到!
             eg:
                 -test
                    -testFile.go
                        package test
                        func tTest(){...}//方法名小写了,导入包后也无法检测到
4.数组
    go语言中的数组结构与传统c语言中的结构类似,但是和JS等弱类型语言却截然不同。
    eg:
        var 数组名 [数组长度]数据类型 = [数组长度]数据类型{初始化的数据内容}
    eg:    
        var arr [10]int = [10]int{10,11,12,13,14,15,16,17,18,19};
    对于go语言而言,数组并不是一个可以存放任意数据类型的复杂数据结构。
    数组能存放的数据的类型在数组被定义的时候就已经被约定了,
    存储约定数据意外的其他数据类型,会导致go语言给出错误提示。

    (1)不同的数组初始化方式
        var arr [长度]数据类型 = [长度]数据类型 {初始化数据内容};
        arr := [长度]数据类型 {初始化数据内容}
        arr := [...]数据类型 {初始化数据内容}
        arr := []数据类型 {初始化数据内容}
    
    (2)数组不能够通过赋值的方式来进行“硬扩充”
        对于弱类型语言中的数组来说,数组的长度是一个动态可变的值。比如在JavaScript中
            var arr = [1,2,3];//数组长度最初只有3
            arr[100] = 10;//此时数组的长度被扩展到了101
        但是对于go语言来说这种方式则一定会报错
            arr := [3]int{1,2,3};
            arr[100] = 10;//报错,数组下标访问越界!
    
    (3)冒泡排序(我居然在这栽了个跟斗,简直瞎眼,事实证明老程序员也会在最基础的地方摔)
        arr := [10]int{9,8,7,6,5,4,3,2,1,0};
        for i:=0; i<len(arr)-1; i++{
            for j:=0; j<len(arr)-1-i; j++{
                if(arr[j]>arr[j+1]){
                    temp := arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
        fmt.Println(arr);
    
    (4)特别注意的是,在go语言中数组作为参数在函数中传入的时候,是值传递!!!
        func allAdd(arr [4]int)(resultArr [4]int){
            for i,_ := range arr{
                arr[i]++;
            }
            return arr;
        }
        func main(){
            arr := [4]int{1,2,3,4};
            newArr := allAdd(arr);
            fmt.Println(arr);//[1,2,3,4] 传入函数后,原数组不会受到任何影响!!
            fmt.Println(newArr);//[2,3,4,5]
        }

5.随机数
    在计算机的编程语言中,随机数的概念其实是不存在的。真正的随机数的名称应当是:概率。
    但是我们可以通过计算机时钟来模拟出随机数的样子,也就是伪随机数。
    而go语言对于伪随机数的构建不像很多弱类型语言一样封装到脖子,让开发者直接使用
    而是需要开发者手动对随机数种子进行时钟混淆,来保证每次获取的样本都不相同。
    eg:
        rand.Seed(time.Now().UnixNano());
        rand.Intn(10);
    在go语言中想要使用随机数需要使用到两个系统库,【math/rand】和【time】.
    其中time库提供时间函数,来作为随机数种子
    而math/rand库则提供随机数函数,从随机数种子混淆后的范围中获取样本。
    eg:
        rand.Seed(time.Now().UnixNano());
        for i:=0;i<100;i++{
            ranNum := rand.Intn(100);
            fmt.Println(ranNum);
        }
    注意:rand.Intn()方法获取的随机数是从下界到上界,但是不包括上界的随机数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值