[go]函数

init执行顺序

对同一个 go 文件的 init( ) 调用顺序是从上到下的
对同一个 package 中的不同文件,将文件名按字符串进行“从小到大”排序,之后顺序调用各文件中的init()函数
对于不同的 package,如果不相互依赖的话,按照 main 包中 import 的顺序调用其包中的 init() 函数
如果 package 存在依赖,调用顺序为最后被依赖的最先被初始化,例如:导入顺序 main –> A –> B –> C,则初始化顺序为 C –> B –> A –> main,一次执行对应的 init 方法

main函数

//main不支持参数,传参使用os.Args
func main() {
	fmt.Println(os.Args)
}

//$: go run main.go 1 2 3
//[/var/folders/4g/nfky9hks3j39qhvz741tmcn40000gn/T/go-build150967295/b001/exe/main 1 2 3]
//main函数不支持返回值

func main() int{
	return 10
}
//$: go run main.go
//# command-line-arguments
//./main.go:3:6: func main must have no arguments and no return values
//main函数返回状态值的方法: os.Exit
func main() {
	fmt.Println("hello")
	os.Exit(-1)
}
//$: go run main.go
//hello
//exit status 255

定义

- 特点
函数是结构化编程的最小模块单元.
将复杂的算法过程分解为若干较小任务, 隐藏相关细节. 使得程序结构更加清晰.便于维护.

普通函数则专注于算法流程,通过接收参数来完成特定逻辑运算,并返回最终结果

支持前置声明
    func main() {
        test()
    }

    func test() {
        println("100")
    }

不支持同名重载
    func test() {
        println("100")
    }

    func test() {
        println("200")
    }

不支持命名嵌套定义
    func main() {
        func test() {
            println("200")
        }
    }
不支持默认参数
支持不定长边参
    func test(a int, n ...int) {
        fmt.Println(a, n) //10 [1 2 3]
    }

    func main() {
        arr := []int{1, 2, 3}
        test(10, arr...)
    }
支持多返回值
支持命名返回值
支持匿名函数和闭包


- 闭包
func test() func() {
	age := 0
	return func() {
		age++
		println(age)
	}
}

func main() {
	f := test()
	f()
	f()
	f()

}


- 延迟调用
func test(fn func()) func() {
	time := 0
	return func() {
		time++
		if time > 2 {
			fn()
		}
	}

}

func main() {
	run := test(func() {
		println("hello world")
	})
	run()
	run()
	run() //在执行第三次时才触发
}

只能和nil比较
func(){} == nil

参数

- 参数
不支持可选,不支持命名实参

按顺序传参数
func test(x, y int, s string, _ bool) *int {
	return nil
}

func main() {
	test(1, 2, "", false) 
}

形参类似是局部变量, 重复声明会出错
    func test(x, y int) {
        x := 100
        var y int
    }


- 无论是哪种类型都传参都是值传递,无非是拷贝的是指(尽管他们指向的同一个对象)针还是值.
func test(x *int) {
	fmt.Printf("%p\n", &x)
}

func main() {
	a := 10
	fmt.Printf("%p\n", &a)
	test(&a)
}
//0xc0000160c0
//0xc000006030


- 参数过多可以放到一个符合类型中, 变向的实现可选参数和命名实参的功能
type serverOption struct{ 
   address string
   port  int
   path  string
   timeout time.Duration
   log    *log.Logger
} 
  
func newOption() *serverOption{ 
   return&serverOption{             // 默认参数 
       address: "0.0.0.0", 
       port:   8080, 
       path:    "/var/test", 
       timeout:time.Second*5, 
       log:    nil, 
    } 
} 
  
func server(option*serverOption) {} 
  
func main() { 
   opt:=newOption() 
   opt.port=8085         // 命名参数设置 
  
   server(opt) 
}



- 可变参数(本质是一个切片,只能放在尾部)
func test(s string,a...int) { 
   fmt.Printf("%T, %v\n",a,a)       // 显示类型和值 
} 
  
func main() { 
   test("abc",1,2,3,4) 
}

- 切片作为变参时,必须进行展开操作, 如果是数组,则将其转换为切片
func test(a...int) { 
   fmt.Println(a) 
} 
  
func main() { 
   a:= [3]int{10,20,30} 
   test(a[:]...)            // 转换为slice后展开 
}

既然变参是切片,那么参数复制的仅是切片自身,并不包括底层数组,也因此可修改原数据。


返回值

必须要有明确的return语句
- 不明确
func test(x int) int {
	if x > 0 {
		return 1
	} else if x < 0 {
		return 0
	}
}

- 多返回值用括号括起来
func div(x,y int) (int,error) { 
   if y==0{ 
       return 0,errors.New("division by zero") 
    } 

- 命名多返回值
func paging(sql string,index int) (count int,pages int,err error) { 
}


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


- 要命名都命名

匿名函数

没有定义名字符号的函数
可以在函数内部定义匿名函数

可以被直接调用
可保存到变量
可以作为参数和返回值


- 直接执行
	func(s string){
		println(s)
	}("hello mm")


- 存储为变量
	s := "hello"
	fn := func(s string) {
		println(s)
	}
	fn(s)

- 作为参数
func test(f func()) { 
   f() 
} 
  
func main() { 
   test(func() { 
       println("hello,world!") 
    }) 
}

- 作为返回值
func test()func(int,int)int{ 
   return func(x,y int)int{ 
       return x+y
    } 
} 
  
func main() { 
   add:=test() 
   println(add(1,2)) 
}


- 匿名结构体类型
   type calc struct{                // 定义结构体类型 
       mul func(x,y int)int         // 函数类型字段 
    } 
  
- 作为chan元素
   x:=calc{ 
       mul:func(x,y int)int{ 
           return x*y
        }, 
    } 

    c:=make(chan func(int,int)int,2) 


- 闭包的延迟特性
函数和其引用环境的组合

func test(x int)func() { 
   return func() { 
       println(x) 
    } 
} 
  
func main() { 
   f:=test(123) 
   f() 
}


延迟调用

defer想当前函数注册时候执行的函数调用
因为他直到当前函数执行结束前才执行
常用语资源释放,解除锁定,错误处理等操作


func main() { 
   f,err:=os.Open("./main.go") 
   if err!=nil{ 
       log.Fatalln(err) 
    } 
  
   defer f.Close()          // 仅注册,直到main退出前才执行 
  
    ...do something... 
}


延迟调用注册的是调用,必须提供执行所需的参数
参数值是在注册时候, 被赋值缓存起来, 如对状态敏感, 可改用指针或闭包
func main() {
	x := 1
	defer println(x) //注册时拷贝一份当前变量值
	x = 100
	println(x)
}
// 100 1


多个延迟注册按FILO次序执行。
func main() { 
   defer println("a") 
   defer println("b") 
}



posted @ 2019-12-07 16:19  mmaotai  阅读(184)  评论(0编辑  收藏  举报