go程序设计笔记(二)函数

226 阅读2分钟

匿名函数

func main() {
    //匿名函数使用
	str := strings.Map(func(r rune) rune {
		return r + 2;
	}, "1234567")
	fmt.Println(str)
	f := squ()
	fmt.Println(f()) //
	fmt.Println(f()) 
	fmt.Println(f())
}

func squ() func() int {
	x:=0
	return func() int {
		x++
		return x * x
	}
}

匿名函数迭代变量问题

func main()  {
	//这个函数存在迭代遍历错误的事例,
	//d是循环生成的变量,在一个作用块进行声明,在循环里面
	//创建的所有函数都使用固定的变量,因为回调函数后面在调用
	//因此回调函数d变量实际取值都是循环的最后一次变量
	//修改方法是在循环里面定义一个临时变量,defer函数也会存在这样的问题

	var rmdirs []func()
	/* 错误实力
	for _,d := range tempDirs() {
		os.MkdirAll(d,0755)
		rmdirs = append(rmdirs, func() {
			os.RemoveAll(d)
		})
	}
	*/
	//修改方法在循环体里定义一个变量
	for _,d := range tempDirs() {
		d:= d
		os.MkdirAll(d,0755)
		rmdirs = append(rmdirs, func() {
			os.RemoveAll(d)
		})
	}


	//一些处理
	for _,rmdir := range rmdirs {
		rmdir()//清理
	}
}

解决这种问题的最简单方式就是在循环里定义一个变量,接受循环的值

defer延迟调用函数

  1. defer延迟调用,相当于使用栈的形式,随着函数的调用接受,先入后出的方式调用defer函数,此函数在循环中也存在上面的迭代变量问题
func main()  {
	bigSlowOperation()
}

func bigSlowOperation()  {
	defer trace("bigSlowOperation")()
	time.Sleep(10*time.Second)
}

func trace(msg string) func() {
	start:= time.Now()
	log.Printf("开始调用函数%s",msg)
	return func() {
		log.Printf("结束调用函数%s,%s",msg,time.Since(start))
	}
}

  1. defer函数在return之后执行,可以拿到return之后变量的值
//需要在这里定义函数返回值的变量名称
func double(x int) (result int)   {
	defer func() {
		fmt.Printf("double(x)=%s",result)
	}()
	return x+x
}
  1. 因为defer是在函数返回之后执行,所以在循环体里面执行的语句,可能会存在问题
//因为defer会在函数调用完成之后执行,文件打开的操作可能会用光文件描述符,改进的方法是讲defer放进一个函数中执行
func f(filenames []string) error  {
	for _,filename :=range filenames {
		file,err:= os.Open(filename)
		if err !=nil {
			return err
		}
		defer file.Close()
	}
	return nil
}
//改进的方式

func f1(filenames []string) error  {
	for _,filename :=range filenames {
		err := dofile(filename)
		if err !=nil {
			return err
		}
	}
	return nil
}
//在这个函数中调用defer,一次循环会调用函数,函数执行完之后就会释放文件描述符,因此就不存在上面的问题
func dofile(filename string) error  {
	file,err := os.Open(filename)
	if err!= nil {
		return err
	}
	defer file.Close()
	return nil
}