Go学习笔记(二):语法基础2
2020-04-09 13:17:12数组
数组的声明
var a [3]int //声明并初始化为默认零值
a[0] = 1
b := [3]int{1,2,3}
c := [...]int{1,2,3,4,5} //不指定元素个数
d := [2][2]int{{1,2},{3,4}} //多维数组初始化
数组元素遍历
func TestTravelArray(t &testing.T) {
a := [...]{1,2,3,4,5}
for idx/*索引*/,elem/*元素*/ := range a {
fmt.PrintIn(idx, elem)
}
}
数组截取
a[开始索引(包含),结束索引(不报销)]
a := [...]int{1,2,3,4,5}
a[1:2] //2
a[1:3] //2,3
a[1:len(a)] //2,3,4,5
a[1:] //2,3,4,5
a[:3] //1,2,3
切片
切片的声明
var s0 []int
s0 = append(s0, 1)
s := []int{}
s1 := []int{1, 2, 3}
s2 := make([]int, 2, 4)
/* []type, len, cap
其中len个元素会被初始化为默认零值,未初始化元素不可以访问
*/
切片共享存储结构
func TestSliceShareMemory(t *testing.T) {
year := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
Q2 := year[3:6]
t.Log(Q2, len(Q2), cap(Q2)) //3,9
summer := year[5:8]
t.Log(summer, len(summer), cap(summer)) //3,7
summer[0] = "Unknow"
t.Log(Q2) //因为是共享存储,上一句修改summer[0]会影响Q2的值,结果 [Apr,May,Unknow]
t.Log(year) //同理year也会受到summer的赋值影响
}
切片 vs 数组
- 容量是否可以伸缩
- 是否可以进行比较
func TestSliceComparing(t *testing.T) {
a := []int{1, 2, 3, 4}
b := []int{1, 2, 3, 4}
// if a == b { //切片只能和nil比较
// t.Log("equal")
// }
t.Log(a, b)
}
// 容量可伸缩实例
func TestSliceGrowing(t *testing.T) {
s := []int{}
for i := 0; i < 10; i++ {
s = append(s, i) //为什么要给s重新赋值呢? 正是因为容量可能伸缩
t.Log(len(s), cap(s))
}
}
// 返回结果
1 1
2 2
3 4
4 4
5 8
6 8
7 8
8 8
9 16
10 16
Map
Map声明
m := map[string]int{"one": 1, "two": 2, "three": 3}
m1 := map[string]int{}
m1["one"] = 1
m2 := make(map[string]int, 10 /*Initial Capacity*/)
//为什么不初始化len?
Map元素的访问
与其他主要编程语⾔言的差异
在访问的 Key 不存在时,仍会返回零值,不能通过返回 nil 来判断元素是否存在
if v, ok := m["four"]; ok {
t.Log("four", v)
} else {
t.Log("Not existing")
}
Map的遍历
m := map[string]int{"one": 1, "two": 2, "three": 3}
for k, v := range m {
t.Log(k, v)
}
Map与工厂模式
- Map的value可以是一个方法
- 与Go的Dock type接口方式一起,可以方便的实现单一方法对象的工厂模式
func TestMapWithFunValue(t *testing.T) {
m := map[int]func(op int) int{}
m[1] = func(op int) int { return op }
m[2] = func(op int) int { return op * op }
m[3] = func(op int) int { return op * op * op }
t.Log(m[1](2), m[2](2), m[3](2))
}
实现Set
Go的内置集合没有Set实现,可以map[type]bool
- 元素的唯一性
- 基本操作
1)添加元素
2)判断元素是否存在
3)删除元素
4)元素个数
func TestMapForSet(t *testing.T) {
mySet := map[int]bool{}
mySet[1] = true
n := 3
if mySet[n] {
t.Logf("%d is existing", n)
} else {
t.Logf("%d is not existing", n)
}
mySet[3] = true
t.Log(len(mySet))
delete(mySet, 1)
n = 1
if mySet[n] {
t.Logf("%d is existing", n)
} else {
t.Logf("%d is not existing", n)
}
}
字符串
与其他主要编程语⾔言的差异
- string 是数据类型,不不是引⽤用或指针类型
- string 是只读的 byte slice,len 函数可以它所包含的 byte 数
- string 的 byte 数组可以存放任何数据
func TestString(t *testing.T) {
var s string
t.Log(s) //初始化为默认零值“”
s = "hello"
t.Log(len(s))
//s[1] = '3' //string是不可变的byte slice
//s = "\xE4\xB8\xA5" //可以存储任何二进制数据
s = "\xE4\xBA\xBB\xFF"
t.Log(s) // 严
t.Log(len(s)) // 3
s = "中"
t.Log(len(s)) //是byte数
c := []rune(s)
t.Log(len(c)) // 1
// t.Log("rune size:", unsafe.Sizeof(c[0]))
t.Logf("中 unicode %x", c[0])
t.Logf("中 UTF8 %x", s)
}
Unicode UTF8
- Unicode是一种字符集(code point)
- UTF8是unicode的存储实现(转换为字节序列的规则)
func TestStringToRune(t *testing.T) {
s := "中华人民共和国"
for _, c := range s {
t.Logf("%[1]c %[1]x", c)
}
}
编码与存储
字符 | “中” |
---|---|
Unicode | 0x4E2D |
utf-8 | 0xE4B8AD |
string/[]byte | [0xE4,0xB8,0xAD] |
常用字符串函数
- strings包(https://golang.org/pkg/strings/)
- strconv包 (https://golang.org/pkg/strconv/)
函数(一等公民)
与其他主要编程语⾔言的差异
- 可以有多个返回值
- 所有参数都是值传递:slice,map,channel 会有传引⽤用的错觉
- 函数可以作为变量量的值
- 函数可以作为参数和返回值
// 多返回值
func returnMultiValues() (int, int) {
return rand.Intn(10), rand.Intn(20)
}
// 函数作为参数和返回值
// 计算函数执行时长。 (类似面向对象语言里面的装饰模式,在原函数上包装新函数)
func timeSpent(inner func(op int) int) func(op int) int {
return func(n int) int {
start := time.Now()
ret := inner(n)
fmt.Println("time spent:", time.Since(start).Seconds())
return ret
}
}
推荐学习函数式编程的书籍
可变参数
func sum(ops ...int) int {
s := 0
for _, op := range ops {
s += op
}
return s
}
defer函数
func TestDefer(t *testing.T) {
defer func() {
t.Log("Clear resources")
}()
t.Log("Started")
panic("Fatal error") //defer仍会执⾏行行
}