《Go程序设计语言》第一,二章总结

第五章:函数
	·当函数存在返回值的时候必须显式地以return语句结束,除非函数明确不会走完整个流程(比如一个for死循环)
	·函数的类型 
		称为函数签名,当一个函数拥有相同的形参列表和返回列表时,认为这两个函数的类型或签名是相同的。形参和返回参数的名称不会影响函数的类型
	·实参是按值传递的,所以函数接收到的是没有实参的副本,修改形参的变量并不会影响到实参的值。
		但是,如果提供的实参是引用类型,比如 指针,slice,map,函数或者通道,那么形参变量就有可能间接的修改实参变量
	·有些函数没有函数体,说明不是用go语言实现的,eg
		package math
		func Sin(x float64) float64  //使用汇编语言实现
	·错误
		·习惯上将错误值作为最后一个结果返回,如果错误只有一个情况,结果通常设置为布尔类型
		·与其他语言不同,go语言通过使用普通变量的值而非异常来报告错误。尽管go有异常机制,但是go语言的异常是针对bug导致的预料外的错误
		·go程序使用通常的控制流机制(比如if和return语句)也对错误
		·错误处理策略:
			当函数调用返回一个错误时,调用者应该负责检查错误并采取合适的处理应对
			·首先最常见的是将错误传递下去,在子例程中发生的错误通过返回值返回给主例程,这样在主获得错误并进行判断处理
		·log.Fatalf("Size is down: %v\n",err) //以这种方式处理错误,它默认会将时间和日期作为前缀添加到错误消息前
		·当读取文件到结束的位置,io包会返回 EOF 错误
			package io
			import "errors"
			var EOF = errors.New("EOF")
			eg: 一段读取文件的错误处理 (伪代码)
				for {
					r,_,err := in.read()
					if (err == io.EOF) {
						break
					}
					if err != nil {
						return fmt.Errorf("read failed: %v",err)
					}
				}
				
	·函数变量:
		var f func(int) int
		func square(n int) int {return n * n}
		func negative(n int) int {return -n}
		func product(m,n int) int {return  m * n}
		func main()  {
			f := square //f的类型已经确定了
			fmt.Println(f(3))
			
			/**
			negative 和 square 的函数类型是一致的,所以f可以完成赋值
			函数的类型 是由形参列表和返回值列表确定的
			 */
			f = negative
			fmt.Println(f(3))
		
			//f = product //编译错误: 不能把类型 func(int,int) int 赋值 给 func (int) int
			//fmt.Println(f(3))
			
			g := product
			fmt.Println(g(2,3))
		}
	
	·不能调用一个空的函数变量
		var f func(int) int //
		func main()  {
			fmt.Println(f(3))
		}
	·defer : 正确使用defer是在成功获得资源之后
		resp,err := http.Get(url)
		if err != nil {
			return err
		}
		defer resp.Body.Close() //确定了成功获取资源之后释放资源
	
	·可以使用改方法来测试一个函数的调用时间
		func main()  {
			defer f1()() //注意这里是两个括号
			time.Sleep(time.Second * 5)
		}
		func f1() func()  {
			start := time.Now() //这个不是延迟执行
			return func() {  //延迟执行的是返回的这个函数
				fmt.Println(time.Since(start))   //可以计算函数的调用时间
			}
		}
	·延迟函数可以获取和改变返回值
		func main()  {
			fmt.Println(double(5))
		}
		func double(x int) (result int) {
			defer func() {
				fmt.Println(x,result)
				result += 100 //延迟函数不到函数的最后一刻是不会执行的
			}()
			return x + x
		}
	·注意延时函数在循环里面的使用
		for _,filename := range filenames {
			f, err := os.Open(filename)
			if err != nil {
				return err
			}
			defer f.Close //这样f.Close根本没有在每次循环的时候关闭,会用尽资源
		}
		解决方案是:将for循环放到一个函数里面,这样函数结束就会执行defer
			for _, filename := range filenames {
				if err := doFile(filename); err != nil {
					return err
				}
			}
			func doFile(filename string) error {
				f,err := os.Open(filename)
				if err != nil {return err}
				defer f.Close() //这样每次执行都会调用到f.Close
			}
			
	·后defer的先执行,相当于一个栈

猜你喜欢

转载自blog.csdn.net/weixin_38104426/article/details/81170111