最近研究室新开始一个基于Hyperledger平台的项目,因此建立了一个Go语言学习小组,打算共同学习一下基础部分。
学习资料:Go编程语言官方指南 https://tour.go-zh.org/welcome/1
1. 基础
1.1 没有分号。
1.2 导入包时,更推荐使用分组导入语句的形式。如果一个名字以大写字母开头,那么它就是已导出的。在导入一个包时,只能引用其中已导出的名字,任何未导出的名字在该包外均无法访问。
import (
"fmt"
"math"
)
1.3 函数可以返回任意数量的返回值,且返回值可以被命名。变量名在前,类型在后。当连续两个或多个函数的已命名形参类型相同时,除最后一个类型以外,其他都可以省略。
func swap(x, y string) (string, string) {
return y, x
}
1.4 var语句用于声明一个变量列表,可以出现在包或函数级别。变量未指定初始值时,默认为0,false和空字符串。若初始值存在,则可以省略类型,变量会从初始值中自动获取类型。在函数中,可以用简洁赋值语句:=代替var声明。但是常量的声明不可以用:=语句,使用const关键字。
var i, j int = 1, 2
const Pi = 3.14
func main() {
k := 3
var c, python, java = true, false, "no!"
fmt.Println(i, j, k, c, python, java, Pi)
}
1.5 在不同类型的项之间赋值时需要显示转换。比如float64转换为uint类型时,必须显示转换。
1.6 Go只有一种循环结构:for循环。for的条件语句没有小括号,而后必须要有大括号,且部分可省略。if语句类似。
func main() {
sum := 1
for ; sum < 1000; {
sum += sum
}
fmt.Println(sum)
}
1.7 switch语句较灵活,只运行选定的case,默认有break语句。除非以fallthrough语句结束case时,会继续运行下一个case。case无需为常量,取值不必为整数。
switch time.Saturday {
case today + 0:
fmt.Println("Today.")
case today + 1:
fmt.Println("Tomorrow.")
default:
fmt.Println("Too far away.")
}
1.8 defer语句会将函数推迟到外层函数返回之后执行。推迟调用的函数其参数会立即求值,但其函数调用会被压入一个栈中,直到外层函数返回前都不会被调用。
1.9 Println打印的每一项之间都会有空行,Printf需要手动输入。Println以分组形式输出时,注意与单独输出时的区别。
//Go指南if和else部分示例代码 第7页
//输出结果为 9 27>=20 20
fmt.Println(pow(3, 2, 10))
fmt.Println(pow(3, 3, 20))
//输出结果为 27>=20 9 20
fmt.Println(
pow(3, 2, 10),
pow(3, 3, 20),
)
1.10 一个结构体(struct)就是一组字段(field),使用Name:语法可以仅赋值部分字段。结构体输出时自带大括号{},数组输出时自带中括号[]。
//输出结果为 {4 0}
v2 = Vertex{X: 4} // Y:0 被隐式地赋予
1.11 数组不能改变大小,因此常用切片。切片通过两个下标来界定,包括第一个元素,但排除最后一个元素。切片下界的默认值为0,上界则是该切片的长度。更改切片的元素会修改其底层数组中对应的元素。
//以下切片等价
a[0:10]
a[:10]
a[0:]
a[:]
1.12 切片拥有长度和容量。切片的长度就是它所包含的元素个数。切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。切片的零值是nil。
s := []int{2, 3, 5, 7, 11, 13}
printSlice(s) //len=6 cap=6 [2 3 5 7 11 13]
// 截取切片使其长度为 0
s = s[:0]
printSlice(s) //len=0 cap=6 []
// 拓展其长度
s = s[:4]
printSlice(s) //len=4 cap=6 [2 3 5 7]
// 舍弃前两个值
s = s[2:]
printSlice(s) //len=2 cap=4 [5 7]
1.13 make函数创建切片,第三个参数为容量,可忽略可指定。
a := make([]int, 5)
printSlice("a", a) //len=5 cap=5 [0 0 0 0 0]
b := make([]int, 0, 5)
printSlice("b", b) //len=0 cap=5 []
c := b[:2]
printSlice("c", c) //len=2 cap=5 [0 0]
d := c[2:5]
printSlice("d", d) //len=3 cap=3 [0 0 0]
1.14 append函数可以为切片追加新的元素。for循环的range形式可以遍历切片或映射,第一个值为当前元素的下标,第二个值为该下标所对应元素的一份副本,可以按需省略变量。
var s []int
printSlice(s) //len=0 cap=0 []
// 添加一个空切片
s = append(s, 0)
printSlice(s) //len=1 cap=1 [0]
// 这个切片会按需增长
s = append(s, 1)
printSlice(s) //len=2 cap=2 [0 1]
// 可以一次性添加多个元素
s = append(s, 2, 3, 4)
printSlice(s) //len=5 cap=6 [0 1 2 3 4]
3. 并发
3.1 Go程(goroutine)是由Go运行时管理的轻量级线程。
3.2 信道是带有类型的管道,可以通过它用信道操作符<-来发送或者接收值。信道初始化时,第二个参数可以作为一个缓冲值。
ch := make(chan int)
ch <- v //将v发送至信道ch
v := <-ch //从ch接收值并赋予v
暂时还没弄明白的点:
1.14 当用append一次性添加多个元素时,len和cap的数值问题。(len=5 cap=6?)
相关回答:https://github.com/golang/tour/issues/398
https://www.dazhuanlan.com/2019/11/19/5dd3c31e70fb0/
好像不是很重要的部分,但是官方回答貌似是每当切片超过其当前容量时,切片的容量通常会增加一倍,预期容量为8?与输出6仍不相符。