Spiga

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. 基本操作
    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)
	}
}

字符串

与其他主要编程语⾔言的差异

  1. string 是数据类型,不不是引⽤用或指针类型
  2. string 是只读的 byte slice,len 函数可以它所包含的 byte 数
  3. 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

  1. Unicode是一种字符集(code point)
  2. 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]

常用字符串函数

  1. strings包(https://golang.org/pkg/strings/
  2. strconv包 (https://golang.org/pkg/strconv/)

函数(一等公民)

与其他主要编程语⾔言的差异

  1. 可以有多个返回值
  2. 所有参数都是值传递:slice,map,channel 会有传引⽤用的错觉
  3. 函数可以作为变量量的值
  4. 函数可以作为参数和返回值
// 多返回值
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仍会执⾏行行
}