Go程序设计语言第一章学习

Gopl Chaper 01 要点

-Go是编译型语言,当你运行go run hello.go,会经历编译,链接,执行。

  • Go原生支持Unicode编码。

  • Go通过package的概念组织代码,每个源代码文件属于某个package。

  • package main比较特殊,它定义了单独执行的程序,而不是一个库,因此main包是程序的入口。

  • 可以使用import的方式引入其它package,但要注意不要少引入或多余引入package,这样程序将不能编译通过。

  • Go不允许未使用的局部变量存在

  • 一个function由关键字func,名字,参数列表,返回列表,函数体

  • Go语言中的语句不需要分号在结尾,除非多条语句写在一行

  • 注意{要跟func在同一行

  • 变量通过var定义:

    1
    var s, sep string

  变量可以在定义时初始化,也可以不进行初始化,未进行初始化的,如果是整数,默认值0,string默认值是 空串""

  • 这四种声明变量的方式都是等效的:

    1
    2
    3
    4
    s := ""
    var s string
    var s = ""
    var s string = ""

    但还是有一定区别的:第一种方式只能在一个function内部使用,不是package层的变量。推荐使用前两种方式进行初始化变量,第一种方式,显示初始化变量,意味着初始化值是很重要的,第二种方式,没有显示初始化值,意味着默认初始化不重要。

  • 字符串的加号相当于拼接

  • i := 1赋值语句,根据右边赋值的类型决定了变量的类型

  • i++作为一种声明,而不是表达式。因此j = i++是不合法的,而且--i形式也不合法

  • Go语言中for循环永远都是没有括号的

    1
    2
    3
    for initialization ; condition ; post {
    // zero or more statements
    }
  • 如果没有initializationpost,那么分号可以省略变成如下形式:

    1
    2
    3
    4
    5
    6
    7
    for condition {
    // zero or more statements
    }
    // a traditional infinite loop
    for {
    // ...
    }
  • 下面代码循环了一个slice

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package main
    import (
    "fmt"
    "os"
    )
    func main() {
    s, sep := "", ""
    for _, arg := range os.Args[1:] {
    s+=sep + arg
    sep = " "
    }
    fmt.Println(s)
    }

    在for循环的每次迭代,range都产生两个值,index和value

  • 下面一段代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package main
    import (
    "bufio"
    "fmt"
    "os"
    )
    func main() {
    counts := make(map[string]int)
    input := bufio.NewScanner(os.Stdin)
    for input.Scan() {
    counts[input.Text()]++
    }
    // NOTE: ignoring potential errors from input.Err()
    for line, n := range counts {
    if n > 1 {
    fmt.Printf("%d\t%s\n", n, line)
    }
    }
    }

    make生成的counts是key为string,value为int的map。input是stdin的输入流。range counts中取出的key为line,value为n

  • 下面一段代码从文件读取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    package main
    import (
    "bufio"
    "fmt"
    "os"
    )
    func main() {
    counts := make(map[string]int)
    files := os.Args[1:]
    if len(files) == 0 {
    countLines(os.Stdin, counts)
    } else {
    for _, arg := range files {
    f, err := os.Open(arg)
    if err != nil {
    fmt.Fprintf(os.Stderr, "dup2: %v\n", err) // %v :any value in a natural for mat
    continue
    }
    countLines(f, counts)
    f.Close()
    }
    }
    for line, n := range counts {
    if n > 1 {
    fmt.Printf("%d\t%s\n", n, line)
    }
    }
    }
    func countLines(f *os.File, counts map[string]int) {
    input := bufio.NewScanner(f)
    for input.Scan() {
    counts[input.Text()]++
    }
    // NOTE: ignoring potential errors from input.Err()
    }

    os.Open返回两个参数,第一个是*os.File,第二个是error type,如果errnil

    这段代码中先进行了countLines方法调用,后定义了countLines方法,在go语言中这种前后顺序是无关紧要的。

  • 再看一段利用ioutils.ReadFile读文件的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package main
    import (
    "fmt"
    "io/ioutil"
    "os"
    "strings"
    )
    func main() {
    counts := make(map[string]int)
    for _, filename := range os.Args[1:] {
    data, err := ioutil.ReadFile(filename)
    if err != nil {
    fmt.Fprintf(os.Stderr, "dup3: %v\n", err)
    continue
    }
    for _, line := range strings.Split(string(data), "\n") {
    counts[line]++
    }
    }
    for line, n := range counts {
    if n > 1 {
    fmt.Printf("%d\t%s\n", n, line)
    }
    }
    }
  • 下面看跟http相关的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package main
    import (
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
    )
    func main() {
    for _, url := range os.Args[1:] {
    resp, err := http.Get(url)
    if err != nil {
    fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
    os.Exit(1)
    }
    b, err := ioutil.ReadAll(resp.Body)
    resp.Body.Close()
    if err != nil {
    fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
    os.Exit(1)
    }
    fmt.Printf("%s", b)
    }
    }

    很好理解,不多解释了

  • 下面看一个跟routine相关的程序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    package main
    import (
    "fmt"
    "io"
    "io/ioutil"
    "net/http"
    "os"
    "time"
    )
    func main() {
    start := time.Now()
    ch := make(chan string)
    for _, url := range os.Args[1:] {
    go fetch(url, ch) // start a goroutine
    }
    for range os.Args[1:] {
    fmt.Println(<-ch) // receive from channel ch
    }
    fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds())
    }
    func fetch(url string, ch chan<- string) {
    start := time.Now()
    resp, err := http.Get(url)
    if err != nil {
    ch <- fmt.Sprint(err) // send to channel ch
    return
    }
    nbytes, err := io.Copy(ioutil.Discard, resp.Body)
    resp.Body.Close() // don't leak resources
    if err != nil {
    ch <- fmt.Sprintf("while reading %s: %v", url, err)
    return
    }
    secs := time.Since(start).Seconds()
    ch <- fmt.Sprintf("%.2fs %7d %s", secs, nbytes, url)
    }

    routine和channel在go语言中都是跟多线程并发相关的很重要的概念,会在后边详细介绍