/* Package regexp implements a simple library for regular expressions. The syntax of the regular expressions accepted is: regexp: concatenation { '|' concatenation } concatenation: { closure } closure: term [ '*' | '+' | '?' ] term: '^' '$' '.' character '[' [ '^' ] character-ranges ']' '(' regexp ')' */ package regexp
文档注释句子尽量完整, 尽量以 可导出的名称为开头, 这样非常方便检索
1 2 3
// Compile parses a regular expression and returns, if successful, a Regexp // object that can be used to match against text. funcCompile(str string) (regexp *Regexp, err error) {
对于组声明的, 注释只需要一个 godoc 会自动映射到整个组
1 2 3 4 5 6
// Error codes returned by failures to parse an expression. var ( ErrInternal = errors.New("regexp: internal error") ErrUnmatchedLpar = errors.New("regexp: unmatched '('") ErrUnmatchedRpar = errors.New("regexp: unmatched ')'") )
// Like a C for for init; condition; post { } // Like a C while for condition { } // Like a C for(;;) for { }
// 遍历切片/数组/字符串/映射/chanel -> range for key, value := range oldMap { newMap[key] = value } // 遍历时候 省略第二个变量 for key := range m { for _, value := range array {
funcunhex(c byte)byte { switch { case'0' <= c && c <= '9': return c - '0' case'a' <= c && c <= 'f': return c - 'a' + 10 case'A' <= c && c <= 'F': return c - 'A' + 10 } // 逗号隔开, 条件罗列 case' ', '?', '&', '=', '#', '+', '%': returntrue } return0 }
// 另一种场景, 选择 type var t interface{} t = functionOfSomeType() switch t := t.(type) { default: fmt.Printf("unexpected type %T", t) // %T prints whatever type t has casebool: fmt.Printf("boolean %t\n", t) // t has type bool caseint: fmt.Printf("integer %d\n", t) // t has type int case *bool: fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool case *int: fmt.Printf("pointer to integer %d\n", *t) // t has type *int }
控制流语句: break continue goto 与 c 区别较大
goto 限定只能同一个 函数内使用, 不能跳过变量声明.
break continue 除了 c 的相同写法,还存在 +tag 的写法.用于一次跳出多层 or 终止多层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
OuterLoop: for i := 0; i < 3; i++ { for j := 0; j < 3; j++ { if i == 1 && j == 1 { continue OuterLoop // 继续外层循环的下一次迭代 } fmt.Println(i, j) } }
OuterLoop: for i := 0; i < 5; i++ { for j := 0; j < 5; j++ { if i*j == 6 { break OuterLoop // 跳出外层循环 } fmt.Println(i, j) } }
Functions
返回多个值… c/c++ 中实现相同定义, 需要传入一堆的指针…
1 2 3 4 5 6
// func (file *File) Write(b []byte) (n int, err error) // 常见的错误排查 n, status := Write(x) if status != OK { xxx }
无参数 return, 这是特别的一点
1 2 3 4 5 6 7 8 9 10 11
// 返回值会关联到 n 和 err funcReadFull(r Reader, buf []byte) (n int, err error) { forlen(buf) > 0 && err == nil { var nr int nr, err = r.Read(buf) n += nr buf = buf[nr:] } // 没有参数 return }
type Transform [3][3]float64// A 3x3 array, really an array of arrays. type LinesOfText [][]byte// A slice of byte slices. // 二维 text := LinesOfText{ []byte("Now is the time"), []byte("for all good gophers"), []byte("to bring some fun to the party."), }
必须分配二维切片
独立分配每一个切片
只有一个数组, 其他切片指向这个数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// Allocate the top-level slice. picture := make([][]uint8, YSize) // One row per unit of y. // Loop over the rows, allocating the slice for each row. for i := range picture { picture[i] = make([]uint8, XSize) }
// Allocate the top-level slice, the same as before. picture := make([][]uint8, YSize) // One row per unit of y. // Allocate one large slice to hold all the pixels. pixels := make([]uint8, XSize*YSize) // Has type []uint8 even though picture is [][]uint8. // Loop over the rows, slicing each row from the front of the remaining pixels slice. for i := range picture { picture[i], pixels = pixels[:XSize], pixels[XSize:] }
const ( // 通过赋予空白标识符来忽略第一个值 _ = iota// ignore first value by assigning to blank identifier KB ByteSize = 1 << (10 * iota) MB GB TB PB EB ZB YB ) // 表达式 YB 会打印出 1.00YB,而 ByteSize(1e13) 则会打印出 9.09TB。 func(b ByteSize) String() string { switch { case b >= YB: return fmt.Sprintf("%fYB", b/YB) case b >= ZB: return fmt.Sprintf("%fZB", b/ZB) case b >= EB: return fmt.Sprintf("%fEB", b/EB) case b >= PB: return fmt.Sprintf("%fPB", b/PB) case b >= TB: return fmt.Sprintf("%fTB", b/TB) case b >= GB: return fmt.Sprintf("%fGB", b/GB) case b >= MB: return fmt.Sprintf("%fMB", b/MB) case b >= KB: return fmt.Sprintf("%fKB", b/KB) } return fmt.Sprintf("%fB", b) }
init 函数
1 2 3 4 5 6 7 8 9 10 11 12 13
funcinit() { if user == "" { log.Fatal("$USER not set") } if home == "" { home = "/home/" + user } if gopath == "" { gopath = home + "/go" } // gopath 可通过命令行中的 --gopath 标记覆盖掉。 flag.StringVar(&gopath, "gopath", gopath, "override default GOPATH") }
Methods
go 可以为任何已经命名的类型定义方法
对应两种类型: 参数是 值 参数是指针;
一般情况下 类型支持寻址 (支持使用指针), 只定义一种方法就行, go 会自动解引用 (*p)/自动取址 (&v), 指针或者变量本身 区别不大
var b ByteSlice // b 是一个值, b.Write(data) // Go自动转换为(&b).Write(data) (&b).Write(data) // 直接使用指针调用
Interfaces and other Types
接口定义行为, 类似鸭子类型;
传入接口的就存在类型判断问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
type Stringer interface { String() string }
var value interface{} // 调用者提供的值 // switch 选择 switch str := value.(type) { casestring: return str case Stringer: return str.String() } // 直接转换然后判断的 str, ok := value.(string) if ok { fmt.Printf("string value is: %q\n", str) } else { fmt.Printf("value is not a string\n") }
type Container struct { Base Name string// 这个 Name 会隐藏 Base.Name }
这么实用主义的特性自然是小心的大量使用了
Concurrency
这一节是 go 的核心, 小心小心;
Do not communicate by sharing memory; instead, share memory by communicating.
通过信道 交换信息 而不是共享内存…
个人愚见: 基于 channel 的并发模型, 拆解了 C 中大量的锁结构, 简化 & 更符合直觉
go func(){xxx}
chanel
1 2 3
ci := make(chanint) // unbuffered channel of integers cj := make(chanint, 0) // unbuffered channel of integers cs := make(chan *os.File, 100) // buffered channel of pointers to Files
// 一种解决办法是 作为参数 值传递进入函数 funcServe(queue chan *Request) { for req := range queue { sem <1 gofunc(req *Request) { process(req) <-sem }(req) } } // 还有就是可以创建 同名变量(还是值拷贝) funcServe(queue chan *Request) { for req := range queue { req := req // 为该 Go 程创建 req 的新实例 | 看起来有些奇怪, 但这是 go 的常见写法 sem <1 gofunc() { process(req) <-sem }() } }
另一种函数是 server 启动协程时候就限制下数量
1 2 3 4 5 6 7 8 9 10 11 12 13
funchandle(queue chan *Request) { for r := range queue { process(r) } }
funcServe(clientRequests chan *Request, quit chanbool) { // Start handlers for i := 0; i < MaxOutstanding; i++ { go handle(clientRequests) } <-quit // Wait to be told to exit. }
// 请求之中就带着一个 channel type Request struct { args []int f func([]int)int resultChan chanint }
// client funcsum(a []int) (s int) { for _, v := range a { s += v } return } request := &Request{[]int{3, 4, 5}, sum, make(chanint)} // 发送请求 clientRequests <request // 等待 chanel 回应 fmt.Printf("answer: %d\n", <-request.resultChan)
// server funchandle(queue chan *Request) { for req := range queue { req.resultChan <req.f(req.args) // server 的回复直接写入到 channel } }
协程可不是仅仅为了方便拆分锁, 更多为了 并行
并行的协程数量为 GOMAXPROCS
在 go 1.5 以后 GOMAXPROCS = cpu 核心数量, 1.5 以前是只有 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14
const NCPU = 4// CPU 核心数
func(v Vector) DoAll(u Vector) { c := make(chanint, NCPU) // 缓冲区是可选的,但明显用上更好 for i := 0; i < NCPU; i++ { // 启动耗时任务 go v.DoSome(i*len(v)/NCPU, (i+1)*len(v)/NCPU, u, c) } // 排空信道。 for i := 0; i < NCPU; i++ { <-c // 等待任务完成 } // 一切完成。 }