第四章(函数)

  • go函数特点
    • 无须前置声明
    • 不支持命名嵌套定义
    • 不支持同名函数重载
    • 不支持默认参数
    • 支持不定长变参
    • 支持多返回值
    • 支持命名返回值
    • 支持匿名函数和闭包
  • func只能判断其是否为nil,不支持其它比较操作
package main

func main() {
	println(a == nil) //false
	println(a == b) //invalid operation: a == b (func can only be compared to nil)
}

func a() {

}

func b() {

}
  • init()函数先于main函数执行,用于初始化

    • init函数没有输入参数、返回值
    • 每个包可以有多个init函数,多个init函数执行顺序go没有明确定义(注意不可依赖init函数执行顺序)
  • 不管是指针、引用类型、还是其它类型参数,都是值拷贝传递(pass-by-value)

  • 变参本质就是一个切片。只能接收一到多个同类型参数,且必须放在列表尾部。

func main() {

	test("abc", 1, 2, 3, 4)

}

func test(s string, a ...int) {
	fmt.Printf("%T,%v\n", a, a) //[]int,[1 2 3 4]
}
  • 将切片作为变参时,须进行展开操作。如果是数组,先将其转换为切片
func main() {
	a := [3]int{1, 2, 3}
	test(a[:]...) //先转换为切片,再展开

}
//可变参数
func test(a ...int) {
	fmt.Printf("%T,%v\n", a, a) //[]int,[1 2 3]
}
  • 命名返回值和参数一样,可当作函数局部变量使用,最有由return隐式返回
func main() {
	res, err := div(2, 0)
	if nil != err {
		println("err")
		os.Exit(-1)
	}
	println(res)
}

func div(x, y int) (z int, err error) {
	if 0 == y {
		err = errors.New("division by zero")
		return //隐式返回: 0,err
	}
	z = x / y
	return //隐式返回:z,nil
}

  • 闭包
    • 闭包是在其词法上下文中引用了自由变量的函数,或者说是函数和其引用的环境的组合体

    eg

        func main() {
        	f := test(0x100)
        	f()
    
        }
        func test(x int) func() {
        	println(&x) //0xc000055f68
        	return func() {
        		println(&x, x) //0xc000055f68 256
        	}
        }
    
    • 粗浅理解:闭包就是返回的匿名函数中引用了目标函数的变量,在main函数中执行时,可以正确读取变量的值。这种现象叫闭包
  • 闭包-“延迟求值”
func main() {
	for _, f := range test() {
		f()
	}
	//0xc000102000 2
	//0xc000102000 2
}
func test() []func() {
	var s []func()
	for i := 0; i < 2; i++ {
		s = append(s, func() {
			println(&i, i)
		})
	}
	return s
}

上面输出的结果是2,因为添加操作仅仅是将匿名函数放入切片,并未执行。因此,当main执行这些函数时,读取的是环境变量i最后一次循环的值2

如果想输出1,2那么给一个中间变量就oj8k了

  func main() {
  for _, f := range test() {
  	f()
  }
  //0xc000018050 0
  //0xc000018058 1
      }
      func test() []func() {
      	var s []func()
      	for i := 0; i < 2; i++ {
      		a := i
      		s = append(s, func() {
      			println(&a, a)
      		})
      	}
      	return s
      }


  • defer函数(延迟调用函数)向当前函数注册稍后执行的函数调用。

  • 错误处理

    eg

      func main() {
      z, err := div(5, 0)
      if err == errDivZero {
      	log.Fatalln(err) //2023/01/05 23:12:27 division by zero
      }
      println(z)
    }
    
    var errDivZero = errors.New("division by zero")
    
    func div(x, y int) (int, error) {
    	if 0 == y {
    
    		return 0, errDivZero
    	}
    	return x / y, nil
    }
    
    
  • panic,recover类型try/catch

    • 连续调用panic,仅最后一个会被recover捕获
        func main() {
        	defer func() {
        		if err := recover(); err != nil { //捕获错误
        			log.Fatalln(err)
        		}
        	}()
        	panic("i am panic")
        	println("here") //永远不会执行
        }
    
    • panic会立即中断当前函数流程,执行延迟调用。而在延迟调用函数中,recover可捕获并返回panic提交的错误对象
    • 无论是否recover,所有延迟调用都会被执行。panic要么被外层捕获,要么导致进程崩溃
posted @ 2023-01-05 23:37  巴达克  阅读(13)  评论(0编辑  收藏  举报