Go 函数,包(二)

#### Go 函数,包(二)***百丈峰,松如浪,地势坤,厚德载物之像***  今天又到周五啦,你们有没有激动呢,反正我很激动,又有两天的自由了;  上一节我们学习了Go 的函数和包的一些知识 , 今天接着学习... ---##### init 函数 每个程序源文件都可以包含一个init 函数, 该函数在main 函数前执行,被Go 运行框架调用;  
package main

import "fmt"

func init(){
   fmt.Print("init ")
}
func main()  {
   fmt.Print("main")
}

  

输出结果: init main  init 函数的注意事项和细节:  1. 如果一个文件同时包含***全局变量定义,init函数和main 函数***则执行的流程为全局变量定义->init函数->main函数;  2. 其主要作用是完成一些初始化工作3. 如果多个包都有init 函数时,执行顺序为导入顺序,如:  A 导入了 B , B 导入了 C , 那么执行顺序为先执行C 中的全局变量定义,init 函数,再执行B 包中的, 最后执行A 包中的; 
package main

import "fmt"
var a = test01()
func init(){
   fmt.Println("init") // 执行顺序2
   fmt.Println(a) // 执行顺序3
}
func test01() int {
   fmt.Println("test01") // 执行顺序1
   return 1
}
func main()  {
   fmt.Println("main") // 执行顺序4
}

  

---##### 匿名函数Go 支持匿名函数,匿名函数也就是没有名字的函数,如果某一个函数希望使用一次,可以使用匿名函数,同时匿名函数也可以实现多次调用;  如果将匿名函数赋值给一个全局变量,那么这个匿名函数就成为了一个全局匿名函数,在程序的有效作用域都有效;
package main

import "fmt"

func main(){
   // 定义时直接调用
   sum := func(a,b int) int {
      return a +b
   }(10,20)
   fmt.Println(sum)
   // 将匿名函数赋值给一个变量,通过变量调用匿名函数
   a := func(a,b int) int {
      return a+b
   }
   sum = a(10,20)
   fmt.Println(sum)
}

  

---##### 闭包闭包实际上就是一个函数和与其相关的引用环境组合的一个整体
package main

import "fmt"

func add()func(int)int{
   var n int = 1
   return func(i int) int {
      n += i
      return n
   }
}
func main(){
   a := add()
   fmt.Println(a(1)) // 2
   fmt.Println(a(10)) //12
   fmt.Println(a(2)) //14
}

  

说明:  1. add 是一个函数,返回的数据类型是一个匿名函数;  2. 返回的匿名函数 引用到add 函数的变量n ,因此返回的匿名函数就和n 形成一个整体,构成闭包;  3. 也可以这样理解: 闭包是类,函数是方法,n 是字段,函数和它使用的n 构成闭包;  4. 因此当反复调用a 函数时, n 将累计;  
package main

import (
   "fmt"
   "strings"
)

func addStr() func(str string) string {
   var base = "a"
   return func(str string) string {
      base += str
      return base
   }
}
// 判断字符串的后缀是否为指定的
func makeSuffix(suffix string) func(string) string {
   return func(s string) string {
      if !strings.HasSuffix(s,suffix) {
         return s + suffix
      }
      return s
   }
}
func main(){
   a := addStr()
   fmt.Println(a("b")) // ab
   fmt.Println(a("c")) // abc
   fmt.Println(a("d")) // abcd
   jpg := makeSuffix(".jpg")
   png := makeSuffix(".png")
   fmt.Println(jpg("a.jpg")) // a.jpg
   fmt.Println(jpg("b"))  //b.jpg
   fmt.Println(png("a.png"))  // a.png
   fmt.Println(png("b"))  //b.png
}

  

1. 返回的匿名函数和参数suffix 变量组合成一个闭包,因此传入一次可以多次调用;  ---##### defer 函数defer 也称为延时执行;  当函数执行到defer 时,暂时不执行,将defer 后的语句压入独立的栈,当函数执行完毕,return 前,再从defer 栈按照先入后出原则方式执行;  
package main

import "fmt"

func add(a,b int) int {
   defer fmt.Println("a=",a)  //10 输出顺序3 , 在将语句放入栈时,相关参数的值会进行拷贝
   defer fmt.Println("b=",b)  //20 输出顺序2 , 在将语句放入栈时,相关参数的值会进行拷贝
   a = 11
   sum := a + b
   defer fmt.Println("sum=",sum) //31  输出顺序1
   return sum
}
func main(){
   sum := add(10,20)
   fmt.Println(sum) //31 输出顺序4
}

  

***在defer 将语句入到栈时,会将相关参数的值进行拷贝***1. 在Go 开发中通常会在创建资源(打开文件,连接数据库,锁资源)后使用defer file.Close() conn.Close()2. 在defer 后,仍可以继续使用已经创建的资源; ---##### 函数参数传递方式 上一节中已经讲过函数参数的值类型与引用类型,我们再来深入总结一下,这个知识点在编译型语言中很重要;  1. 函数参数是值类型时就是值传递,函数参数是引用类型就是引用传递; 2. 不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是值传递是值的拷贝,引用传递是地址的拷贝;  3. 一般情况下地址拷贝效率高,而值拷贝由参数的数据大小, 数据越大,效率越低; 4. 值类型: 基本数据类型int 系列, float 系列, bool, string, 数组,结构体;  5. 引用类型: 指针,slice 切片, map , chan ,interface ;   
package main

import "fmt"

// 值传递
func test01(a int){
   fmt.Printf("[test01] a value= %d a address=%p\n",a,&a)
}
// 对于值传递希望更改原来的值可以传入变量地址
func test03(a *int){
   *a = 100
   fmt.Printf("[test03] c value=%v c address=%p\n",*a,&a)
}
// 引用传递
func test02(a []int){
   fmt.Printf("[test02] b value=%v b address=%p\n",a,&a)
}
func main(){
   var a int = 10
   fmt.Printf("a value= %d a address=%p\n",a,&a)
   test01(a)
   var b = []int{1,2}
   fmt.Printf("b value=%v b address=%p\n",b,&b)
   test02(b)
   var c = 10
   test03(&c)
   fmt.Printf("c value=%v c address=%p\n",c,&c)
}

  

--- ##### 变量的作用域 1. 函数内部声明或定义的变量是局部变量,作用域限于函数内部;  2. 函数外部声明或定义的变量是全局变量,作用域在整个包有效,若首字母大写,则作用域为整个程序;  3. 如果变量声明或定义在代码块内,如: if/for 代码块内,则作用域仅限于代码块; 
package main

import "fmt"

// 全局变量
var name = "golang"
// 如果首字母大写,则在整个程序中有效,其它包也可以使用
var Age = 22

func test01(){
   // 局部变量
   var a int = 10
   fmt.Println(a)
   // 使用全局变量
   fmt.Println(name)
   if a > 2 {
      // 代码块内部的局部变量, 仅限于if 代码块有效
      var d int = 100
      fmt.Println(d)
   }
   // 代码块内部的局部变量, 仅限于if 代码块有效
   //fmt.Println(d) // error
}
func main(){
   // 局部变量
   var b int = 1
   fmt.Println(b)
   // 使用全局变量
   fmt.Println(name)
   test01()
}

  个人微信公众号上有最新文章: 欢迎大家关注一同学习交流

原文地址:https://www.cnblogs.com/Mail-maomao/p/11411767.html

时间: 2024-10-10 04:56:20

Go 函数,包(二)的相关文章

ioutil包二

ioutil包二 (原创随笔,转载请注明出处 http://www.cnblogs.com/majianguo/p/8016426.html) ioutil包实现了一些I/O实用功能,导出了7个函数和1个变量: func NopCloser(r io.Reader) io.ReadCloser func ReadAll(r io.Reader) ([]byte, error) func ReadDir(dirname string) ([]os.FileInfo, error) func Rea

04_内置函数(二)

内置函数(二) 1.1 callable() 功能:函数是否可调用 示例1: def f1(): pass f1() f2 = 123 f2() # 输出结果 TypeError: 'int' object is not callable 示例2: def f1(): pass # f1() f2 = 123 # f2() print(callable(f1)) print(callable(f2)) # 输出结果 True False 1.2 chr() 功能:数字转字母,返回对应的ASCII

总结文件操作函数(二)-C语言

格式化读写: #include <stdio.h> int printf(const char *format, ...);                   //相当于fprintf(stdout,format,-); int scanf(const char *format, -); int fprintf(FILE *stream, const char *format, ...);      //中间的参数为写入文件的格式 int fscanf(FILE *stream, const

黑马程序员——c语言学习心得——函数传递二维数组

黑马程序员——c语言学习心得——函数传递二维数组 -------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 一.定义指针的时候一定要初始化.   变量定义的时候给变量初始化,这是保证不出错的一个很好的习惯.尤其是在指针的使用上,如果我们没有给指针初始化,就会出现野指针,该指针的指向并不是我们所希望的,一旦错误的释放了这个指针,就会发生内存的访问.那么如何初始化指针变量呢,一般有以下几种方法:   1.初始化空指针   int* pInteger=N

数组合并函数,二维数组相同字段合并到一起。

一般从数据库中提取数据时,会遇到各种各样类型的数据,要求也不尽相同.自己这两天开发的时候遇到一个很纠结的问题,如下: 比如一个二维数组是这样的: Array ( [0] => Array ( [uid] => 231 [username] => 123456 [active] =>aaaa [transfer] =>1111 ) [1] => Array ( [uid] => 231 [username] =>123456 [active] => bb

PHP_I love U之(1)php衣食父母:使用usort()函数为二维数组排序

<?php //PHP_I love U之(1)php衣食父母:使用usort()函数为二维数组排序 //PHP语言中使用usort()函数 为 二维数组排序: // [][]==  a[][1]= 长度; a[][2] = views点击数 $AAlong=10; //变量$AAlong 定义数组长度 //for ($i=0;$i<$AAlong;++$i)  //多维数组扩展时使用- //{ for($j=0;$j<$AAlong;++$j) //for j22  //这段给数组(用

ORACLE SQL单行函数(二)【weber出品必属精品】

11.dual:虚表,任何用户都可以使用,表结构如下: SQL> desc dual Name Null? Type ----------------------------------------- -------- ---------------------------- DUMMY VARCHAR2(1) 12.dual的作用: 1. 查询数据库系统日期 2. 进行四则运算 SQL> select sysdate from dual; ---这里查询数据库系统日期 SYSDATE ---

函数(二)

上节回顾: 1.为何用函数: 解决代码重用问题 提高代码可维护性 程序的组织结构清晰,可读性强 2.定义函数 !!!先定义,后使用 def funcname(arg1,arg2,...): """描述信息""" 函数体 return value 无参, def foo(): print('foo') 有参, def my_sum(x,y): return x+y 空 3.调用函数 语句形式:foo() 表达式形式:res=my_sum(1,2)

编译器对C++ 11变参模板(Variadic Template)的函数包扩展实现的差异

编译器对C++ 11变参模板(Variadic Template)的函数包扩展实现的差异 题目挺绕口的.C++ 11的好东西不算太多,但变参模板(Variadic Template)肯定是其中耀眼的一颗明星,在C++设计新思维中,你可以看到很多模版的代码为了支持不确定的参数个数,而要重载1个参数到N个模板参数的N个函数.虽然种代码一般也是用会用宏和脚步辅助生成.但我想也没有人愿意看到几千行这种单调的函数.通过这个东东,模板的威力可以爆发. 目前的最新的编译器基本都已经支持Variadic Tem