利用周末的时间,简单的学习了一下go的语法。go的语言设计的基本理念是“简洁”。从关键字的设计上来说,也是非常的少,只有20多个。 语法上面虽然没有python简洁,但是总的来说,也是非常不错了。应该开发同样的逻辑,比c/c++的代码量少非常多。
语法相对友好,上手比较快之外,go还保持了不错了的性能。goroutine的设计的理念非常非常的牛逼,学名叫做CSP Communicating Sequential Processes(顺序通信处理), 通过共享的方式来做线程之间数据的同步。
除此之外,godoc,严格的语法检查,比如未使用的变量会报错,交叉编译等机制。可以保证一个大型项目的代码更加易于维护,保证一个项目组的开发人员写的代码在可维护的水准之上。
go的基本语法
- 数组
package main
import "fmt"
func main() {
p := []int{2, 3, 5, 7, 11, 13}
// 打印整个数组
fmt.Println("p == ", p)
// 切片
fmt.Println("p[1:4] ==", p[1:4])
// 省略下标代表从 0 开始
fmt.Println("p[:3] ==", p[:3])
fmt.Println("p[4:] ==", p[4:])
// 使用make 创建数组
b := make([]int, 0, 5) // 长度len(b)=0, 容量cap(b)=5
fmt.Println("b == ", b)
}
- 指针
var p *int
i := 42
p = &i
fmt.Println(*p) // 通过指针 p 读取 i
*p = 21 // 通过指针 p 设置 i
指针和结构体
package main
import "fmt"
// 结构体的定义
type Vertex struct {
X int
Y int
}
func main() {
fmt.Println(Vertex{1, 2})
// 定义结构体
var v Vertex
v = Vertex{1, 2}
fmt.Println(v)
// 定义指针
var pv *Vertex
pv = &v
fmt.Println(*pv)
}
如果是c语言切换过来的人,估计在这个地方会有一些困惑,为什么指针是通过 . 的方式来访问成员的,而不是通过 -> 的方式?!
type Square struct {
side int32
}
func (sq *Square) Area() int32 {
return sq.side * sq.side
}
func main {
s1 := Square{200} // 这个按照c的标准应该是值的引用
s2 := &Square{300} // 指针
// 为什么可以都可以用这样的方式去访问变量和函数? !
fmt.Println("side %d, area %d", s1.side, s1.Area())
fmt.Println("side %d, area %d", s2.side, s2.Area())
}
查了一下原因:
在 Go 语言中这叫 选择器(selector)。无论变量是一个结构体类型还是一个结构体类型指针,都使用同样的 选择器符(selector-notation) 来引用结构体的字段:
type myStruct struct { i int }
var v myStruct // v是结构体类型变量
var p *myStruct // p是指向一个结构体类型变量的指针
v.i
p.i
虽然语法的表述是一样的,但是内部实现的方式是:
![](https://img.haomeiwen.com/i4717565/e0e4d461e812f9bb.png)
结构体的方法
go中结构体的方法的语法形式是:
package main
import "fmt"
type TwoInts struct {
a int
b int
}
func main() {
two1 := new(TwoInts)
two1.a = 12
two1.b = 10
fmt.Printf("The sum is: %d\n", two1.AddThem())
fmt.Printf("Add them to the param: %d\n", two1.AddToParam(20))
two2 := TwoInts{3, 4}
fmt.Printf("The sum is: %d\n", two2.AddThem())
}
func (tn *TwoInts) AddThem() int {
return tn.a + tn.b
}
func (tn *TwoInts) AddToParam(param int) int {
return tn.a + tn.b + param
}
字典 map
package main
import "fmt"
func main() {
// 创建一个字典可以使用内置函数make
// "make(map[键类型]值类型)"
m := make(map[string]int)
// 使用经典的"name[key]=value"来为键设置值
m["k1"] = 7
m["k2"] = 13
// 用Println输出字典,会输出所有的键值对
fmt.Println("map:", m)
// 获取一个键的值 "name[key]".
v1 := m["k1"]
fmt.Println("v1: ", v1)
// 内置函数返回字典的元素个数
fmt.Println("len:", len(m))
// 内置函数delete从字典删除一个键对应的值
delete(m, "k2")
fmt.Println("map:", m)
// 根据键来获取值有一个可选的返回值,这个返回值表示字典中是否
// 存在该键,如果存在为true,返回对应值,否则为false,返回零值
// 有的时候需要根据这个返回值来区分返回结果到底是存在的值还是零值
// 比如字典不存在键x对应的整型值,返回零值就是0,但是恰好字典中有
// 键y对应的值为0,这个时候需要那个可选返回值来判断是否零值。
_, ok := m["k2"]
fmt.Println("ok:", ok)
// 你可以用 ":=" 同时定义和初始化一个字典
n := map[string]int{"foo": 1, "bar": 2}
fmt.Println("map:", n)
}
包
- import 自定义的包
$ tree
.
├── foo
│ └── test.go
└── main.go
main.go
package main
import "fmt"
import "./foo"
func main() {
bar.Abc()
fmt.Print("This is main\n")
}
test.go
package bar
import "fmt"
func Abc() {
fmt.Print("This is test print\n")
}
# 执行
go run main.go
接口与反射
看了一下go的语法,感觉go接口的设计,有一点想c++的函数指针。 (上层的模块在init的时候,传递给底层的模块一个函数指针,底层模块调用的时候,根据注册的函数指针,调用不同的函数。)
channel
默认的channel是阻塞式的,如果读取一个没有数据的channel的数据,它将会被阻塞;如果向channel发送数据,它将会被阻塞,直到数据被读出来。
package main
import "fmt"
import "time"
func test_func(a int, c chan int){
c <- a // 写入数据,阻塞
fmt.Println("test_func 1 ")
c <- a*2 // 再次写入数据,又阻塞
fmt.Println("test_func 2 ")
fmt.Println("end")
}
func main() {
fmt.Println("begin ~~")
c := make(chan int)
go test_func(10, c)
time.Sleep(3 * time.Second) // sleep 3 second
fmt.Println("begin ~~~~~ ")
x := <- c // 读数据,channel那端的写入操作才会继续往下执行
fmt.Println("end : ", x)
// main 函数结束,程序结束
}
有缓存的channel
ch := make(chan type, value)
value指定了channel里面的元素的数量。比如value=5,那么前五个元素的写入是不会阻塞的。但是,第六个元素的写入会阻塞。
- 错误处理
- gorouting
gorouting的协程机制,可以非常简单的实现一个 “限频器”。比如,一个消息消费的接口的qps是1000。为了保护它不超过限制,写一个简单的例子:
tick := time.Tick(1 * time.Second)
for {
<-tick
fmt.Println("tick")
// dosomething, proc msg , tick一秒被唤醒一次,也就是一秒处理一条消息;
}
time.Tick() 是golang内置的一个接口,简单的原理是利用timer向chan里面周期性的写入一个消息。然后,消费消息的chan会阻塞在chan的另外一端阻塞读消息。这里可以先挖个坑,golang 的源码里面的timer模块可以拿出来讲一下。
参考 :
网友评论