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

FrankAdd · · 533 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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()方法获取的随机数是从下界到上界,但是不包括上界的随机数。

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

533 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传