funcgrade(score int)string { g := "" switch { case score < 0 || score > 100: panic(fmt.Sprintf("Wrong score: %d", score)) case score < 60: g = "F" case score < 80: g = "C" case score < 90: g = "B" case score <= 100: g = "A" } return g }
循环
for
for不需要括号
for 可以省略初始条件(相当于 while)、结束条件、递增表达式
for n := 100 ; n > 0; n /= 2 { // todo }
for scanner.Scan() { fmt.Println(scanner.Text()) }
函数
返回值类型写在最后面(类似 typescript)
函数可以返回多个值(一般用法为第二个参数返回 error)
函数返回多个值时可以起名
函数可以作为参数
有可变参数列表
有匿名函数
没有默认参数、可选参数、函数重载等
funceval(a, b int, op string) (int, error) { switch op { case"+": return a + b, nil case"-": return a - b, nil case"*": return a * b, nil case"/": q, _ := div(a, b) return q, nil default: return0, fmt.Errorf("unsupported operation: %s", op) } }
funcdiv(a, b int) (q, r int) { return a / b, a % b }
funcsum(numbers ...int)int { s := 0 for i := range numbers { s += numbers[i] } return s }
funcapply(op func(int, int)int, a, b int) int { p := reflect.ValueOf(op).Pointer() opName := runtime.FuncForPC(p).Name() fmt.Printf("Calling func %s with args (%d, %d)\n", opName, a, b) return op(a, b) }
fmt.Println(apply(func(a int, b int)int { returnint(math.Pow(float64(a), float64(b))) }, 3, 4))
// 指定初始 len s2 := make([]int, 16) // 指定初始 len cap s3 := make([]int, 10, 32)
append
有内建函数:
s = append(s, val)
添加元素时如果超越了 cap,系统会重新分配更大的底层数组
由于值传递的关系,必须接收 append 的返回值
copy
copy(s2, s1)
delete
删除下标为 3 的元素:
s2 = append(s2[:3], s2[4:]...)
shift/pop
// shift front := s2[0] s2 = s2[1:] // pop tail := s2[len(s2)-1] s2 = s2[:len(s2)-1]
Map
操作
创建:make(map[string]int)
获取:m[key],字符不存在返回 zero value
判断 key 是否存在:value, ok := m[key]
删除:delete(m, key)
遍历:for k, v := range m,无序的
获取长度:len(m)
map 使用哈希表,key 必须可以比较相等,除了 slice map function 以外的内建类型都可以作为 key,不包含上述字段的 struct 也可以
例:寻找最长的不含有重复字符的子串
funclongest(s string)int { lastOccurred := make(map[byte]int) start := 0 maxLength := 0 for i, c := range []byte(s) { if last, ok := lastOccurred[c]; ok && last >= start { start = last + 1 } if (i - start + 1) > maxLength { maxLength = i - start + 1 } lastOccurred[c] = i } return maxLength }
String
for i, b := range []byte(s) 得到的是 8 位 byte
for i, b := range []rune(s) 得到的是 utf8 解码后的字符
获取 utf8 字符串长度:utf8.RuneCountInString(s)
字符串操作库:strings.ToUpper / strings.xxx
面向对象
仅支持封装,不支持继承和多态
没有 class,只有 struct
struct
无需关注结构体是储存在栈还是堆上
知识点:nil 指针也能调用方法
定义
值定义与成员方法的定义方式与传统方式有区别
成员方法定义只有使用指针接收者(引用传递)才能改变结构的内容
结构过大要考虑使用指针接收者(拷贝成本)
注意方法的一致性:最好要么都是指针接收者,要么都是值接收者
type TreeNode struct { value int left, right *TreeNode }
deferfunc() { e := recover() if err, ok := e.(error); ok { fmt.Println("catch error: ", err) } else { panic(errors.New("don't know what to do: " + fmt.Sprintf("%v", e))) } }()
//panic(errors.New("...")) //panic(123) b := 0 a := 5 / b fmt.Println(a) //catch error: runtime error: integer divide by zero
funcworker(id int, c chanint) { // 当 channel 收到值,且未关闭时 for n := range c { fmt.Printf("worker %d receive %c\n", id, n) } }
// 返回值为只收 channel funccreateWorker(id int)chan<- int { c := make(chanint) go worker(id, c) return c }
funcchannelDemo() { var cs = make([]chan<- int, 10) for i := 0; i < 10; i++ { cs[i] = createWorker(i) } for i := 0; i < 10; i++ { // 向 channel 发送数据 cs[i] <- 'a' + i } for i := 0; i < 10; i++ { cs[i] <- 'A' + i } time.Sleep(time.Millisecond) }
等待任务结束
方式一:使用 channel
type worker struct { id int in chanint done chanbool }
funcdoWork(w worker) { for n := range w.in { fmt.Printf("worker %d receive %c\n", w.id, n) w.done <- true } }
funccreateWorker(id int) worker { w := worker{ in: make(chanint), done: make(chanbool), id: id, } go doWork(w) return w }
funcchannelDemo() { var w = make([]worker, 10) for i := 0; i < 10; i++ { w[i] = createWorker(i) } for i, worker := range w { worker.in <- 'a' + i } for _, worker := range w { <-worker.done } for i, worker := range w { worker.in <- 'A' + i } for _, worker := range w { <-worker.done } }
funcmain() { channelDemo() }
方式二:使用 WaitGroup
type worker struct { id int in chanint done func() }
funcdoWork(w worker) { for n := range w.in { fmt.Printf("worker %d receive %c\n", w.id, n) w.done() } }
funccreateWorker(id int, wg *sync.WaitGroup) worker { w := worker{ in: make(chanint), done: wg.Done, id: id, } go doWork(w) return w }
funcchannelDemo() { var w = make([]worker, 10) wg := sync.WaitGroup{}
for i := 0; i < 10; i++ { w[i] = createWorker(i, &wg) } //wg.Add(20) for i, worker := range w { wg.Add(1) worker.in <- 'a' + i //wg.Add(1) 错误!应该先 Add 再发数据 } //wg.Wait() for i, worker := range w { wg.Add(1) worker.in <- 'A' + i } wg.Wait() }
func(node *Node) TravelWithChannel() chan *Node { c := make(chan *Node) gofunc() { deferclose(c) node.TravelFunc(func(node *Node) { c <- node }) }() return c }
funcmain() { var root tree.Node // ... max := 0 node := root.TravelWithChannel() for n := range node { if n.Value > max { max = n.Value } } fmt.Println("Max: ", max) }
使用 select 进行调度
select 的使用,可以不加锁控制任务的执行
定时器的使用:定时器返回的也是 channel
在 select 中使用 nil channel:nil channel 永远不会被 select
tm := time.After(time.Second * 10) tick := time.Tick(time.Second) for { var activeChannel chan<- int var activeValue int iflen(values) > 0 { activeChannel = w.in activeValue = values[0] }
select { case <-tm: fmt.Println("program exit") return case n := <-c1: values = append(values, n) //fallthrough case n := <-c2: values = append(values, n) case activeChannel <- activeValue: values = values[1:] case <-tick: fmt.Println("len(values)=", len(values)) case <-time.After(time.Millisecond * 800): fmt.Println("timeout") }
}
传统的同步机制
WaitGroup
Mutex
Cond
如果不加锁,使用 -race 执行会发生 data race:
type atomicInt struct { Value int Lock sync.Mutex }
funcmsgGen(id string) <-chanstring { c := make(chanstring) i := 0 gofunc() { for { time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond) c <- fmt.Sprintf("generator %s sended %d", id, i) i++ } }() return c }
fanIn
将多个 channel 的消息合并为一个输出以避免阻塞:
funcfanIn(channels ...<-chanstring) <-chanstring { c := make(chanstring) for _, ch := range channels { gofunc(ch <-chanstring) { for { c <- <-ch } }(ch) } return c }
funcmain() { m := fanIn( msgGen("svc1"), msgGen("svc2"), msgGen("svc3"), msgGen("svc4"), msgGen("svc5"), ) for { fmt.Println(<-m) } }
fanIn by select
funcfanInBySelect(c1 <-chanstring, c2 <-chanstring) <-chanstring { c := make(chanstring) gofunc() { for { select { case n := <-c1: c <- n case n := <-c2: c <- n } } }() return c }
任务的控制
非阻塞等待
funcnoneBlockingWait(c <-chanstring) (string, bool) { select { case n := <-c: return n, true default: return"", false } }
超时机制
functimeoutWait(c <-chanstring, timeout time.Duration) (string, bool) { select { case n := <-c: return n, true case <-time.After(timeout): return"", false } }
优雅退出
当消息的内容无所谓时,channel 可以用空的 struct,体积比 boolean 更小。
funcmsgGen(id string, done chanstruct{}) <-chanstring { c := make(chanstring) i := 0 gofunc() { for { select { case <-done: fmt.Println("cleaning...") time.Sleep(time.Second * 2) fmt.Println("clean done.") done <- struct{}{} return case <-time.After(time.Duration(rand.Intn(2000)) * time.Millisecond): c <- fmt.Sprintf("generator %s sended %d", id, i) i++ } } }() return c }
funcmain() { done := make(chanstruct{}) m1 := msgGen("svc1", done) for i := 0; i < 5; i++ { if msg, ok := timeoutWait(m1, 1*time.Second); ok { fmt.Println("msg from svc1: ", msg) } else { fmt.Println("timeout") } } done <- struct{}{} <-done } //msg from svc1: generator svc1 sended 0 //timeout //msg from svc1: generator svc1 sended 1 //timeout //msg from svc1: generator svc1 sended 2 //cleaning... //clean done.