The Go Programming Language(读书笔记)

The Go Programming Language

目录

 [隐藏] 
  • 1 Tutorial
  • 2 程序结构
  • 3 基本数据类型
  • 4 复合类型
  • 5 函数
  • 6 方法
  • 7 Interfaces
  • 8 Goroutines and Channels
  • 9 并发with共享变量
  • 10 包与Go工具
  • 11 Testing
  • 12 反射
  • 13 底层编程

[编辑]Tutorial

  1. os.Args, slice, ~JS ArrayView?, [begin, end) 这其实是Python语法吧?
  2. 如果变量没有显示初始化,则实际上初始化为改type的零值,对string就是""
  3. for i=0; i
  4. 不合法:y=x++ 或 ++x
  5. for _, arg := range os.Args[1:] {
  6. => strings.Join(os.Args[1:], " ")
  • counts := make(map[string]int) //寻找重复的行;
    1. input := bufio.NewScanner(os.Stdin) //想起了Java的StringTokenizer或C++的cin
    2. for input.Scan() { counts[input.Text()]++ }
  • fmt.Printf:
    1.  %d %x %f ... %t(true/false)
    2.  %c Unicode码点
    3.  %q 双引号括起来的string
    4.  %v 自然格式
    5.  %T 类型
  • f, err := os.Open(path) #注意,f的类型是*os.File
    1. 直接读取整个文件内容:data, err := ioutil.ReadFile(path)
  • 图像编码的例子:Go类库太强大了
  • Fetching a URL
    1. res, err := http.Get(url) #注意,这里能处理https连接
    2. b, err := ioutil.ReadAll(res.Body) #这里Body是一个流对象
    3. 并发化:
      1. ch := make(chan string)
      2. go fetch(url, ch) #启动一个goroutine
      3. <-ch #其实会在这里阻塞等待吧?
      4. func fetch(url string, ch chan<- string) {
        1. ch <- fmt.Sprint(err)
  • Web服务器
    1. http.HandleFunc("/", handler)
    2. func handler(w http.ResponseWriter, r *http.Request) { //后者参数是一个*struct类型,而前者是个引用(复合类型?)
      1. r.URL.Path
    3. var mu sync.Mutex #用于保护goroutine's对共享变量的访问
  • [编辑]程序结构

    1. 名字
    2. 声明:var const type func
      1. package + import:借鉴Java的?
      2.  :=是声明,=是赋值
        1. i, j = j, i
        2. 重名的第2次:=是赋值(但是必须至少声明一个新变量,wtf??)
    3. 指针
      1. x := 1; p := &x; *p = 2
      2. nil
      3. 可安全地返回局部变量的地址 => Pointer aliasing
      4. *p++ //不改变指针p的值
      5. new函数
      6. 变量的生命周期(要求编译器进行‘逃逸分析’)
    4. 赋值
      1. v, ok = x.(T) //m[key], <-ch
      2. _, err = io.Copy(dst, src)
    5. 类型声明
      1. type Celsius float64 //每个类型T对应一个类型转换T(x),允许转换当且仅当类型兼容
        1. func (c Celsius) String() string { return fmt.Sprintf("%g C", c) }
    6. 包与文件
      1. 包级变量的初始化;多个.go文件的导入顺序;func init() {...}
    7. Scope(编译期的名字解析)
      1. shadow
      2. else if嵌套在if中,可看到前者声明的变量
      3. 一个奇怪的例子:var cwd string; func init() { cwd, err := os.Getwd() //错误,全局变量cwd在init里没有被初始化

    [编辑]基本数据类型

    1. 整数
      1. int/uint{8,16,32,64}, int
      2. rune(=int32):Unicode码点
      3. byte(=uint8)
      4. uintptr(参考Low Level Programming章节)
      5. x &^ y(用y来bit clear x, 都为1则清除,否则保持不变, 与非?) vs 同或?
      6. len()返回int而不是uint是有一些额外的好处的。。。
      7. %#[1]x //[1]代表使用Printf的第一个操作数,#代表增加前缀0/0x
    2. 浮点数
      1. 使用float64
      2. fmt.Println(z, -z, 1/z, -1/z, z/z); //"0 -0 +Inf -Inf NaN"
    3. 复数
      1. import ("image", "image/color", "image/png", "math/complx", "os")
    4. Booleans
    5. 字符串
      1. UTF-内部编码:第i个字节不是第i个字符
      2. 无法修改:s[0]='L'; 但是可以重新引用:s+=", hi"
      3. "..."里的\xxx转义序列
      4. raw string literal:`...` (反引号,这里没C++11复杂,但是简单)多行时\r会被删除(留下\n?)
        1. 用于写正则表达式?
      5. Unicode
        1. UTF-8是Go语言的作者Ken Thompson和Rob Pike发明的:
          1. 使用1~4个字节,第1个字节的前缀分别为0 110 1110 11110,后面的都是10(最多可用21个比特的空间?不过还可以继续扩展)
          2. No rune's encoding is a substring of any other, so you can search by just bytes
          3. \uhhhh \Uhhhhhhhh
          4. "世界" = "\xe4\xb8\x96\xe7\x95\x8c" = "\u4e16\u754c"
          5. >=256的单个rune不能用"\xe4\xb8\x96"的形式表示
          6. utf8.RuneCountInString(s) 统计字符的个数
          7. 挨个解码:rune, size := utf8.DecodeRuneInString(s[i:]); i += size
            1. => for i, r := range s { ...
            2. => r := []rune(s)
          8. 如果遇到错误:产生一个替换字符, '\uFFFD'
      6. *Strings and Byte Slices
        1. bytes.Buffer的WriteRune方法?专用的序列化
    6. 常量
      1. type Weekday int; const ( Sunday Weekday = iota, Monday, ... ) //常量生成器
      2. * untyped constants:常量表达式?

    [编辑]复合类型

    1. 数组
      1. q := [...]int{1,2,3} //类型是[3]int,数组的长度是类型的一部分
      2. r := [...]int{99: -1} //前99个元素都是0
      3. import "ctypto/sha256"; c1 = sha256.Sum256([]byte("x")); //%x可以16进制打印全部元素
    2. 切片
      1. s[i:j], where 0<=i<=j<=cap(s)
      2. 不可比较(可能包含自身?‘间接的’),bytes.Equal可用于比较2个[]byte,但其他的需要自己写
      3. *测试为空:len(s)==0 不是s==nil
      4. 创建切片:make([]T, len, cap)
      5. append(slice并不是纯粹的引用类型)
        1. x = append(x, x...)
    3. Maps
      1. map元素不是变量,不能用&获取地址
      2. for k, v := range m { ...
      3. in := bufio.NewReader(os.Stdin); for { r, n, err := in.ReadRune(); if err!=io.EOF && err==nil { ...
        1. if r==unicode.ReplacementChar && n==1 { continue }
    4. 结构
      1. p := &Point{1,2}
      2. 匿名域(相当于OOP里子类重用基类的成员)
        1. 进一步的,任何类型都可以作为匿名域嵌入,哪怕没有subfields(这个有点像traits/mixin的概念)
    5. JSON
      1. data, err := json.Marshal(obj) //内部使用了反射机制(编译型的语言支持运行时反射??)
      2. if err := json.Unmarshal(data, &obj); err!=nil { ...
    6. 文本与HTML模板
      1. const t = `... {{.Name}} ... {{range .Items}} ... {{end}}`
      2. tpl := template.Must(template.New("escape").Parse(t)) ==> tpl.Execute(os.Stdout, dataIn)

    [编辑]函数

    1. 可变大小的栈?!(这样看来,递归不用担心栈溢出了)
    2. 多返回值
    3. 显示错误处理:os.Open不仅会报告错误的原因,还会输出文件名
    4. io.EOF
    5. 函数作为值
    6. 匿名函数
      1. os.MkdirAll(dirpath, 0755) //循环变量每次会被更新,重命名到一个新的局部变量(使用go, defer时会遇到)
    7. Variadic函数
      1. func sum(vals ...int) int { ... } //<== sum(values...)
        1. ...interface{}
        2. ==> func sum(vals ...int) (result int) { defer func() { fmt.Printf(result) }() ... } //AOP
    8. 推迟的函数调用
      1. defer resp.Body.Close()
    9. Panic
      1. var scheme = regexp.MustCompile(`^https?:`)
      2. n := runtime.Stack(buf[:], false); os.Stdout.Write(buf[:n])
    10. Recover
      1. 例如,net/http包可调用recover()从用户handler的panic中恢复(?)

    [编辑]方法

    1. func (p *Point) ScaleBy(factor float64) { ... } //名称是(*Point).ScaleBy
      1. 隐式转换:var p Point; ...; p.ScaleBy(2); //等于(&p).ScaleBy(2)
        1. 不能用于结构成员或slice元素,因它们是‘临时变量’,不能&取地址
    2. nil
    3. The type of an anonymous field may be a pointer to a named type, in which case fields and methods are promoted indirectly from the pointed-to object. (方法嵌入struct)
    4. 方法的值与表达式
      1. f := (*Point).ScaleBy //selector语法,这实际上是一个独立的函数,有别与C++里的成员函数指针?
    5. 封装(struct的小写字母开头的域外部不可见)

    [编辑]Interfaces

    1. var _ io.Writer = (*bytes.Buffer)(nil)
    2. 案例分析:flag.Value
    3. 接口的值(类型描述符)
      1. dynamic dispatch
    4. 接口类型不可比较,会panic
    5. sort.Interface
    6. http.Handler
      1. type Handler interface { ServeHTTP(w ResponseWriter, r *Request) }
      2. func ListenAndServe(address string, h Handler) error
      3. ServeMux(就是一个微型的Web框架?)
        1. Go语言不需要Web框架...
        2. mux.Handle("/list", http.HandlerFunc(db.list)) //注意,http.HandlerFunc是一个类型转换
          1. func (db database) list(w http.ResponseWriter, r *http.Request) { ... }
        3. => mux.HandleFunc("/list", db.list)
      4. 注意:每个handler在单独的goroutin中执行
    7. error
    8. 案例分析:表达式求值
    9. *类型断言
      1. if w, ok := w.(*os.File); ok { ...
    10. *类型开关

    [编辑]Goroutines and Channels

    1. time.Now().Format("15:04:05") //基于实例的格式化,参考时间:“Mon Jan 2 03:04:05PM 2006 UTC-0700”
    2. 并发的Echo:
      1. io.Copy(c, c) #c net.Conn
    3. 1*time.Second //time.Duration类型; ==> time.Sleep(delay)
    4. ch = make(chan int) //Unbuffered, 仅仅用于同步?ch <- 1; <-ch
      1. ch <- struct{}{}
    5. 如果sender知道没有更多数据发送,它可以close(ch)以通知接受者:msg, ok := <-ch
    6. 单向的Channel类型
      1. chan<- int和<-chan int
    7. 缓冲的Channels
      1. ch = make(chan string, 10) //满时发送阻塞,空时接受者阻塞
      2. 示例:返回DNS查询最快的结果(代码略)
    8. sync.WaitGroup:对goroutine的启动/结束进行计数
      1. wg.Add(1) wg.Done() wg.Wait() //哈,就是个信号量嘛, Add(1)必须在go启动worker前调用,不然不能保证Add(1)在主Wait()前执行
      2. 可用于限制并发的IO操作数
    9. 示例:并发的Web爬虫
      1. too parallel ==> 计数chan可用于代表资源限制
    10. select
      1. tick := time.Tick(10*time.Second) ==> <-tick //倒计时 ==> time.NewTicker?可Stop()
      2. select {
        1. case <-ch1: ...
        2. case x := <-ch2: ...a
        3. case ch3 <- y: ...
        4. case <-time.After(10*time.Second) //time.After返回一个channel,同时内部启动一个goroutine向其发送超时通知
    11. 示例:并发的目录遍历
      1. 嗯?可以直接对一个chan做for range迭代?
    12. *取消
      1. select里的default:分支代表什么意思?没有任何case channel就绪?
      2. **调试:利用panic dump
    13. 示例:Chat Server
      1. 每种特定类型的消息就是一个channel?coming、leaving、msg

    [编辑]并发with共享变量

    1. 并发安全:竞争条件、死锁、活锁、饥饿
    2. 共享变量的monitor goroutine ==> Share by communicating
    3. 互斥:sync.Mutex
      1. 保护对共享变量的访问,‘临界区域’
      2. defer m.Unlock() //宁可临界区域大一些,避免panic导致的问题?
      3. *Mutex不可重入
    4. *读写锁:sync.RWMutex
    5. 内存同步
      1. CPU/Compiler乱序执行调度带来的额外问题
    6. sync.Once
      1. loadIconsOnce.Do( loadIcons )
    7. 竞争检测
      1. go build/run/test -race
    8. *示例:并发非阻塞Cache(???)
    9. Goroutines与线程
      1. 可增长的栈:2KB --> 1GB
      2. m:n调度
      3. GOMAXPROCS
      4. Goroutines没有Identity:Simpler设计,函数参数直接影响行为

    [编辑]包与Go工具

    1. Import路径
      1. 域名指定全称?Go Tool负责解析处理?
    2. package声明
      1. main()
      2. _test.go
    3. 重命名import
    4. 空import
      1. import _ "image/png" //仅仅为了执行其init()?Register PNG Decode();
    5. 包与命名
    6. Go工具
      1. $ export GOPATH=$HOME/gobook
      2. $ go get gopl.io/...
      3. $ go env
      4. $ go build ./src/gopl.io/ch1/helloworld //本地路径需要以./开始
        1. *_linux.go
        2. // +buils linux darwin
    7. *Documenting
      1. $ go doc ...
    8. 内部包
      1. net/http/internal/chunked仅可被net/http或net/http/httputil导入
    9. $ go list ...
      1. $ go list -f '模板:.ImportPath -> 模板:Join .Imports " "' compress/...

    [编辑]Testing

    1. func TestNameOrFeature(t *testing.T) { ... t.Error(`...`) ...
    2. 代码覆盖
      1. $ go test -v -run=Coverage -cover-profile=c.out ...
      2. $ go tool cover -html=c.out
    3. Benchmark函数
    4. Profiling
      1. $ go test -cpuprofile=cpu.out -memprofile=mem.out --blockprofile=block.out
    5. *Example函数

    [编辑]反射

    1. reflect.Type/Value
    2. 示例:编码S-表达式
    3. *访问struct域的tag(让我想起了JAXB)

    [编辑]底层编程

    1. unsafe.Sizeof / Alignof / Offsetof
    2. unsafe.Pointer
      1. 可将*float64转换为*uint64,以查看浮点数的位模式
      2. 虽然当前Go并不使用moving GC...
    3. **用cgo来调用C语言代码(略)

    你可能感兴趣的:(读书笔记)