极客时间 《Go 语言从入门到实战》学习笔记。
开发环境
环境配置略。
go1.8 以后不需要设置环境变量。
数据类型
声明
由于 go 中一个赋值语句可以对多个变量进行赋值,因此交换变量的值可写作:
func TestExchange(t *testing.T) {
a := 1
b := 2
a,b = b,a
t.Log(b,a)
}
常量
可以使用 iota 设置连续的值
const (
Monday = iota + 1
Tuesday
Wednesday
Thursday
)
const (
Open = 1 << iota
Close
Pending
)
func TestXx(test *testing.T) {
test.Log(Monday, Tuesday, Wednesday)
test.Log("-----")
test.Log(Open, Close, Pending)
test.Log(Pending | Close)
}
基本类型
- bool
- string
- int unit int64 int32 等
- byte
- rune
- float32 float64
- complex64 complex128
- 不支持隐式类型转换。
- 不支持指针运算
type MyInt int
func TestImplicit(t *testing.T) {
var a MyInt = 1
var b int = 111
t.Log(a + MyInt(b))
}
func TestPoint(t *testing.T) {
a := 1
aPtr := &a
t.Log(a, aPtr)
t.Logf("%T %T", a, aPtr)
}
func TestString(t *testing.T) {
var s string
t.Log(s)
t.Log(len(s))
}
运算符
算术运算符
go 没有前置的 ++ , -- 如 ++a, --b
比较运算符:
== != > < >= <=
==比较数组,go 中:
相同维数,并且个数相同的才可以比较
每个元素相同的数组才相等
func TestCompareArr(t *testing.T) {
a := [...]int{1, 2, 3, 4}
c := [...]int{1, 2, 3, 4}
t.Log(a == c)
}
逻辑运算符
略
位运算符 & | ^ << >>
按位清零, 1 表示将表达式左边对应的位 清 0
1 &^ 0 -- 1
1 &^ 1 -- 0
0 &^ 1 -- 0
0 &^ 0 -- 0
条件和循环
for
举例:
1). while 条件循环 条件 n < 5
n := 0
for n < 5 {
n++
t.Log(n)
}
t.Log("----")
2). 无限循环
n = 0
for {
n++
if n > 5 {
break
}
t.Log(n)
}
if
if 支持变量赋值
func TestIf(t *testing.T) {
if a := 1 == 1; a {
t.Log("true")
}
}
switch
- 不限制常量或者整数
- 不需要 case break
- 支持表达式
func TestSwitch(t *testing.T) {
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X")
case "linux":
fmt.Println("Linux")
default:
fmt.Printf("%s", os)
}
num := 99
switch {
case 0 <= num && num <= 88:
t.Log("A")
default:
t.Log("B")
}
switch num {
case 0, 2:
t.Log("B3")
case 100, 99:
t.Log("B2")
default:
t.Log("B1")
}
}
数组和切片
数组
数组声明, 并赋值
var a [3]int
a[0] = 1
t.Log(a)
数组声明的同时进行赋值
b := [3]int{1, 2, 3}
c := [2][2]int{{1, 2}, {3, 4}}
t.Log(b, c)
数组遍历,直接访问下标或者 foreach
arr3 := [...]int{1, 2, 3, 4, 5, 6}
for i := 0; i < len(arr3); i++ {
t.Log(arr3[i])
}
t.Log("----")
for index, e := range arr3 {
t.Log(index, e)
}
t.Log("----")
for _, e := range arr3 {
t.Log(e)
}
数组的截取操作
a := [...]int{1, 2, 3, 4, 5, 6, 7}
t.Log(a[1:2])
t.Log(a[1:len(a)])
t.Log(a[:])
切片
切片的数据结构: 指针 、长度、 容量
var s0 []int
t.Log(s0, len(s0), cap(s0))
s0 = append(s0, 1)
t.Log(s0, len(s0), cap(s0))
s2 := make([]int, 3, 5)
t.Log(s2, len(s2), cap(s2))
s2 = append(s2, 8)
t.Log(s2, len(s2), cap(s2))
s1 := []int{}
for i := 0; i < 10; i++ {
s1 = append(s1, i)
t.Log("--", len(s1), cap(s1))
}
append 返回切片的好处是多个切片可以共享一个结构,节省了资源。
map
map 声明
m := map[string]int{"one": 1, "two": 2}
m1 := map[string]int{}
t.Log(m)
t.Log(m1)
初始化 capcity
m2 := make(map[string]int, 10 )
t.Log(m2)
m2["11"] = 16
检验空值
不存在的 key 返回零值,不会报异常。
m1 := map[int]int{}
t.Log(m1[1])
m1[3] = 100
if v, ok := m1[3]; ok {
t.Log(ok, ". value is ", v)
} else {
t.Log("key 3 not existing.")
}
遍历
range
func TestTravelMao(t *testing.T) {
m := map[string]int{"one": 1, "two": 2}
for k, v := range m {
t.Log(k, v)
}
}
map 与工厂模式
- map 的 value 可以是一个方法
- 与 Go 的 Dock type 接口方式一起,可以方便的实现单一方法对象的工厂模式
m := map[int]func(op int) int{}
m[1] = func(op int) int {
return 10 * op
}
m[2] = func(op int) int {
return op
}
t.Log(m, m[1](1), m[2](2))
实现 Set
没有内置的 Set ,可以用 map[type]bool
- 元素唯一性
- 基本操作 a.添加 b.判断元素是否存在 c.删除元素 d.元素个数
mySet := map[int]bool{}
mySet[1] = true
n := 1
if mySet[n] {
t.Logf("%d is exsisting", n)
} else {
t.Logf("%d not exsisting", n)
}
delete(mySet, 1)
string
和其他编程语言的差异
- string 是数据类型,不是引用或者指针类型
- string 是只读的 byte slice, len 函数返回其包含的 byte 数
- string 的 byte 数组可以存放任何数据
初始化
初始化默认值为""
var s string
t.Log(s)
s = "hello world"
t.Log(s, len(s))
s = "\xE4\xB8\xA5"
t.Log(s, len(s))
string 是不可变的 byte slice
//s[1] = '3'
编译报错
Unicode 和 UTF8
- unicode 是一种字符集(code point)
- utf8 是 unicode 的存储实现(转换为字节序列的规则)
s = "中"
t.Log(len(s))
c := []rune(s)
t.Log(c)
t.Logf("中 unicode %x", c[0])
t.Logf("中 utf8 %x", s)
遍历字符串的每个 rune
sx := "中国"
for _, c := range sx {
t.Logf("%[1]c %[1]d %[1]x", c)
}
字符串的一些方法
s1 := "A,B,C"
parts := strings.Split(s1, ",")
for i, part := range parts {
t.Log(part, i)
}
joinded := strings.Join(parts, "-")
t.Logf(joinded)
s2 := strconv.Itoa(10)
t.Log(s2)
if i, err := strconv.Atoi("10"); err == nil {
t.Log(10 + i)
}
function
go 中,函数是一等公民。
与其他语言的相比:
- 支持多个返回值
- 所有参数都是值传递: 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 slowFunc(op int) int {
time.Sleep(time.Second * 1)
return op
}
timeSpent(slowFunc)(1)
可变参数
func Sum(opts ...int) int {
ret := 0
for _, op := range opts {
ret += op
}
return ret
}
t.Log(Sum(1, 2, 3))
defer
func TestDefer(t *testing.T) {
defer func() {
t.Log("Clean resources")
}()
t.Log("Started")
panic("Fatal error")
}