go编译型语言,so函数编写的顺序无关紧要。
goroutine 协程
Go语言里面拥三种类型的函数:
-普通的带有名字的函数
-匿名函数或者 lambda 函数
-方法
同一种类型返回值和带有变量名的返回值
func name()(int,int){return a,b}
func name()(a , b int){return}
不可以:
func name()(a,b int,int){return}
定义函数变量:
var fname func()
匿名函数篇:
① 函数内部定义函数:闭包
好处:内联在函数中,不需要声明,可直接使用函数的变量
Go语言中闭包是引用了自由变量的函数,被引用的自由变量和函数一同存在,即使已经离开了自由变量的环境也不会被释放或者删除,在闭包中可以继续使用这个自由变量
函数 + 引用环境 = 闭包
一个函数类型就像结构体一样,可以被实例化,函数本身不存储任何信息,只有与引用环境结合后形成的闭包才具有“记忆性”,函数是编译期静态的概念,而闭包是运行期动态的概念。
闭包(Closure)在某些编程语言中也被称为 Lambda 表达式。
闭包对环境中变量的引用过程也可以被称为“捕获”,在 C++11 标准中,捕获有两种类型,分别是引用和复制,可以改变引用的原值叫做“引用捕获”,捕获的过程值被复制到闭包中使用叫做“复制捕获”。
// C++ 与 C# 中为闭包创建了一个类,而被捕获的变量在编译时放到类中的成员中,闭包在访问被捕获的变量时,实际上访问的是闭包隐藏类的成员。
在闭包内部修改引用的变量
闭包对它作用域上部的变量可以进行修改,修改引用的变量会对变量进行实际修改,通过下面的例子来理解:
例子:
// 准备一个字符串
str := "hello world"
// 创建一个匿名函数
foo := func() {
// 匿名函数中访问str
str = "hello dude"
}
// 调用匿名函数
foo()
被捕获到闭包中的变量让闭包本身拥有了记忆效应,闭包中的逻辑可以修改闭包捕获的变量,变量会跟随闭包生命期一直存在,闭包本身就如同变量一样拥有了记忆效应。
② 函数内部调用其他函数:函数调用
③ 函数内部调用参数传过来的函数:回调函数
将一个函数的指针作为参数传递给另一个函数,在外部再定义这个函数的实现。
回调函数例子:
func visit(list []int, f func(int)) {
for _, v := range list {
f(v)
}
}
func main() {
// 使用匿名函数打印切片内容,可改变传入函数的实现
visit([]int{1, 2, 3, 4}, func(v int) {
fmt.Println(v)
})
}
④ 函数内部调用自己这个函数:递归
go接口篇:
定义接口:
type Invoker interface {
// 需要实现一个Call方法
Call(interface{}) //这个传参??
}
1.结构体实现接口
type Struct struct {
}
// 实现Invoker的Call
func (s *Struct) Call(p interface{}) {
fmt.Println("from struct", p)
}
main函数中:
// 声明接口变量
var invoker Invoker
// 实例化结构体
s := new(Struct)
// 将实例化的结构体赋值到接口
invoker = s
// 使用接口调用实例化结构体的方法Struct.Call
invoker.Call("hello")
2.函数体实现接口
函数的声明不能直接实现接口,需要将函数定义为类型后,使用类型实现结构体,当类型方法被调用时,还需要调用函数本体。
// 函数定义为类型
type FuncCaller func(interface{})
// 实现Invoker的Call
func (f FuncCaller) Call(p interface{}) {
// 调用f()函数本体
f(p)
}
main函数中:
// 声明接口变量
var invoker Invoker
// 将匿名函数转为FuncCaller类型, 再赋值给接口
invoker = FuncCaller(func(v interface{}) {
fmt.Println("from function", v)
})
// 使用接口调用FuncCaller.Call, 内部会调用函数本体
invoker.Call("hello")
TODO:标记部分代码以供将来参考:优化和改进的领域、可能的更改、要讨论的问题等。
Go语言goroutine和channel使用
goroutine是Go语言中的轻量级线程实现,由Go语言运行时(runtime)管理。使用的时候在函数前面加“go”这个单词作为关键词,也是与普通函数的区别了。在函数前面加go关键字就可以创建一个新的goroutine进行并发执行。
channel是Go语言提供的goroutine间的通信方式,我们可以使用channel在两个或多个goroutine之家传递消息。channel使用的关键字是用“chan”.
go方法:
go语言中函数的概念和c语言中的函数类似,函数名其实是一个指针,而go语言的方法是拥有接收者的函数,其实是c++中类的方法的概念。函数是独立存在的,而方法必须有接收者,即必须依附于某个对象。go语言使用struct来抽象对象。因此方法的接收者可以是struct实例或struct的指针。
type user struct {
name string,
email string,
}
//这是函数的定义
func notify(email string) {
fmt.Println("Email is %s", email)
}
//这是方法的定义
func (u user) notify(email string) {
fmt.Println("Email is %d", email)
}
go error类型:
见go入门。
对方法的接收者传值:不改变接收者。传地址才改变。
defer:
Go语言的 defer 语句会将其后面跟随的语句进行延迟处理,在 defer 归属的函数即将返回时,将延迟处理的语句按 defer 的逆序进行执行,也就是说,先被 defer 的语句最后被执行,最后被 defer 的语句,最先被执行。
当有多个 defer 行为被注册时,它们会以逆序执行(类似栈,即后进先出)
defer func(){ }
go func(){ }
go中{}后的():