Go 语言基础语法 | 青训营笔记

92 阅读6分钟

Go 语言基础语法 | 青训营笔记

这是我参与「第五届青训营」伴学笔记创作活动的第 1 天

一、本堂课重点内容

  • 变量
  • if-else
  • 循环
  • switch
  • 数组
  • 切片
  • 函数
  • 结构体
  • 字符串

二、详细知识点介绍

Hello World

package main

import "fmt"

func main() {
    fmt.Println("Hello World!")
}

运行 go 文件

go run file.go

变量

变量声明用 var 关键字

声明变量

// 单变量声明
var firstName string
// 同类型多变量声明
var firstName, lastName string
// 不同类型多变量声明
var firstName, lastName string
var age int
// 多类型多变量声明(推荐方式)
var (
    firstName, lastName string
    age int
)

初始化变量

初始化的时候可以 不标出类型,go 会自行推断。

var (
    firstName = "John"
    lastName  = "Doe"
    age       = 32
)

简写方式

firstName, lastName := "John", "Doe"
age := 32

if-else

x := 27
if x%2 == 0 {
    fmt.Println(x, "is even")
}

复合 if 语句

if num := givemeanumber(); num < 0 {
    fmt.Println(num, "is negative")
} else if num < 10 {
    fmt.Println(num, "has only one digit")
} else {
    fmt.Println(num, "has multiple digits")
}

循环

Go 只使用一个 循环构造,即  for  循环。

分号 (;) 分隔  for  循环的三个组件:

  • 在第一次迭代之前执行的初始语句(可选)。
  • 在每次迭代之前计算的条件表达式。 该条件为  false  时,循环会停止。
  • 在每次迭代结束时执行的后处理语句(可选)。

两个关键字

  • break:跳出循环
  • continue:跳出当前循环

基础用法

for j := 7 ; j <9 ; j++ {
    fmt.Println(j)
}

无限循环

for {
    fmt.Println("loop")
    break
}

只有条件判断

类似气体语言的 while

for i < = 3 {
    fmt.Println(i)
    i=i+1
}

switch

基础 switch 语法

a := 2
switch a {
    case 1:
        fmt.Println("one")
    case 2:
        fmt.Println("two")
    case 3:
        fmt.Println("three")
    case 4,5:
        fmt.PrintLn("four or five")
    default:
        fmt.Println("other")
}

使用条件判断

t := time.Now()
switch {
    case t.Hour()<12:
        fmt.Println("It's before noon")
    default:
        fmt.PrintLn("It's after noon")
}

数组

要在 Go 中声明数组,必须定义其元素的数据类型以及该数组可容纳的元素数目。

cities := [5]string{"New York", "Paris", "Berlin", "Madrid"}
fmt.Println("Cities:", cities)

切片

切片和数组很像,或者说是更高级的数组。切片是动态的,可以随时增加或减少元素。

可以用 make 来创建切片

a := make([]int, 5)
printSlice("a", a)

还可以用切片向的方式来取值

fmt.Println("a[1:4]", a[1:4])

切片的常用方法

// append
a = append(a, 0)
// copy
copy(a, b)

map

map 是一种无序的键值对的集合。键值对中的键和值都是任意类型的。

var m map[string]int
m = make(map[string]int)
m["k1"] = 7
m["k2"] = 13
fmt.Println("map:", m)

range

range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。

m := map[string]int{"k1": 1, "k2": 2}
for k, v := range m {
    fmt.Println("key:", k, "value:", v)
}

函数

函数允许你将一组可以从应用程序的其他部分调用的语句组合在一起。 你可以使用函数来组织代码并使其 更易于阅读 ,而不是创建包含许多语句的程序。 更具 可读性的代码也更易于维护

func plus(a int, b int) int {
    return a + b
}

函数返回多个值(非 return)

func calc(number1 string, number2 string) (sum int, mul int) {
    int1, _ := strconv.Atoi(number1)
    int2, _ := strconv.Atoi(number2)
    sum = int1 + int2
    mul = int1 * int2
    return
}

指针

将值传递给函数时,该函数中的每个更改都不会影响调用方。 Go 是“按值传递”编程语言。 每次向函数传递值时,Go 都会使用该值并创建本地副本(内存中的新变量)。 在函数中对该变量所做的更改都不会影响你向函数发送的更改。

在 Go 中,有两个运算符可用于处理指针:

  • &  运算符使用其后对象的地址。
  • *  运算符取消引用指针。 也就是说,你可以前往指针中包含的地址访问其中的对象。
package main

import "fmt"

func main() {
    firstName := "John"
    updateName(&firstName)
    fmt.Println(firstName)
}

func updateName(name *string) {
    *name = "David"
}

结构体

结构体是一种聚合数据类型,它将多个不同类型的数据组合在一起。 结构体是一种值类型,它的值可以通过值传递或通过引用传递。

type Person struct {
    Name string
    Age  int
}

结构体的方法

func (p *Person) updateName(name string) {
    p.Name = name
}

错误处理

Go 语言中的错误处理是通过返回值来实现的。 一个函数可以返回多个返回值,其中最后一个返回值是 error 类型,这样就可以通过返回的 error 来判断函数是否执行成功。

func openFile(filename string) (*os.File, error) {
    f, err := os.Open(filename)
    if err != nil {
        return nil, err
    }
    return f, nil
}

字符串

Go 语言中的字符串是一个字节的切片,因此可以像切片一样进行操作。 但是,Go 语言中的字符串是不可变的,也就是说字符串一旦创建了就不能修改。

func main() {
    s := "hello"
    c := []byte(s)
    c[0] = 'c'
    s2 := string(c)
    fmt.Printf("%s\n", s2)
}

JSON 处理

Go 语言中的 JSON 处理是通过标准库中的 encoding/json 包来实现的。 该包提供了两个函数 MarshalUnmarshal,分别用于序列化和反序列化。

时间处理

Go 语言中的时间处理是通过标准库中的 time 包来实现的。 该包提供了一个 Time 类型,该类型的值代表了一个时间点。

三、实践练习例子

猜数字游戏

改进:

  • 用 Scanf 函数从命令行读取用户输入的数字
  • switch 语句改写 if-else 语句
package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    maxNum := 100
    rand.Seed(time.Now().UnixNano())
    secretNumber := rand.Intn(maxNum)
    // fmt.Println("The secret number is ", secretNumber)

    fmt.Println("Please input your guess")
    var guess int
Loop:
    for {
        _, err := fmt.Scanf("%d", &guess)
        switch {
        case err != nil:
            fmt.Println("Invalid input. Please enter an integer value", err)
        case guess > secretNumber:
            fmt.Println("Your guess is bigger than the secret number. Please try again")
        case guess < secretNumber:
            fmt.Println("Your guess is smaller than the secret number. Please try again")
        default:
            fmt.Println("Correct, you Legend!")
            break Loop
        }
        fmt.Scanln()
    }
}

词典增加引擎

  • 增加有道词典的引擎
  • 增强封装
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    "strconv"
)

type YouDaoDictRequest struct {
    Num     int    `json:"num"`     // 返回结果数量
    Ver     int    `json:"ver"`     // 版本号
    Doctype string `json:"doctype"` // 返回结果类型
    Cache   bool   `json:"cache"`   // 是否使用缓存
    Le      string `json:"le"`      // 查询词语的语言
    Q       string `json:"q"`       // 查询词语
}

// TODO 封装有道词典查询 request
func queryYouDaoDict(word string) *http.Request {
    dict := YouDaoDictRequest{Num: 5, Ver: 3, Doctype: "json", Cache: false, Le: "en", Q: word}
    url := "https://dict.youdao.com/suggest"
    method := "GET"
    req, err := http.NewRequest(method, url, nil) //TODO 发送 GET 请求
    if err != nil {
        log.Fatal(err)
    }
    // NOTE 添加查询参数
    query := req.URL.Query()
    // "https://dict.youdao.com/suggest?num=5&ver=3.0&doctype=json&cache=false&le=en&q=hello"
    query.Add("num", strconv.Itoa(dict.Num))
    query.Add("ver", strconv.Itoa(dict.Ver))
    query.Add("doctype", dict.Doctype)
    query.Add("cache", strconv.FormatBool(dict.Cache))
    query.Add("le", dict.Le)
    query.Add("q", dict.Q)
    req.URL.RawQuery = query.Encode()
    return req
}

type YouDaoDictResponse struct {
    Result struct {
        Msg  string `json:"msg"`  // 返回结果信息
        Code int    `json:"code"` // 200 为成功,其他为失败
    } `json:"result"`
    Data struct {
        Entries []struct { // 词典查询结果列表
            Explain string `json:"explain"`
            Entry   string `json:"entry"`
        } `json:"entries"`
        Query    string `json:"query"`    // 查询词语
        Language string `json:"language"` // 查询词语的语言
        Type     string `json:"type"`
    } `json:"data"`
}

func destructureYouDao(word string, bodyText []byte) {
    var dictResponse YouDaoDictResponse
    err := json.Unmarshal(bodyText, &dictResponse)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(word)
    for _, item := range dictResponse.Data.Entries {
        fmt.Println(item.Explain)
    }
}

type CaiYunDictRequest struct {
    TransType string `json:"trans_type"` // 翻译类型,en2zh 为英译中,zh2en 为中译英
    Source    string `json:"source"`     // 待翻译的文本
}

// TODO 封装彩云词典查询 request
func queryCaiYunDict(word string) *http.Request {
    request := CaiYunDictRequest{TransType: "en2zh", Source: word}
    buf, err := json.Marshal(request)
    if err != nil {
        log.Fatal(err)
    }
    data := bytes.NewReader(buf)
    url := "https://api.interpreter.caiyunai.com/v1/dict"
    method := "POST"
    req, err := http.NewRequest(method, url, data) //TODO 发送 POST 请求
    if err != nil {
        log.Fatal(err)
    }
    // NOTE 添加请求头
    req.Header.Set("Content-Type", "application/json;charset=UTF-8")
    req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
    return req
}

type CaiYunDictResponse struct {
    Rc   int `json:"rc"`
    Wiki struct {
        KnownInLaguages int `json:"known_in_laguages"`
        Description     struct {
            Source string      `json:"source"`
            Target interface{} `json:"target"`
        } `json:"description"`
        ID   string `json:"id"`
        Item struct {
            Source string `json:"source"`
            Target string `json:"target"`
        } `json:"item"`
        ImageURL  string `json:"image_url"`
        IsSubject string `json:"is_subject"`
        Sitelink  string `json:"sitelink"`
    } `json:"wiki"`
    Dictionary struct {
        Prons struct {
            EnUs string `json:"en-us"`
            En   string `json:"en"`
        } `json:"prons"`
        Explanations []string      `json:"explanations"`
        Synonym      []string      `json:"synonym"`
        Antonym      []string      `json:"antonym"`
        WqxExample   [][]string    `json:"wqx_example"`
        Entry        string        `json:"entry"`
        Type         string        `json:"type"`
        Related      []interface{} `json:"related"`
        Source       string        `json:"source"`
    } `json:"dictionary"`
}

func destructureCaiYun(word string, bodyText []byte) {
    var dictResponse CaiYunDictResponse
    err := json.Unmarshal(bodyText, &dictResponse)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
    for _, item := range dictResponse.Dictionary.Explanations {
        fmt.Println(item)
    }
}

func query(word string, dictRequest func(string) *http.Request, destructure func(string, []byte)) {

    client := &http.Client{} //TODO 使用 http.Client 发送请求
    req := dictRequest(word)
    // NOTE 发送请求
    resp, err := client.Do(req)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
    bodyText, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
    }
    if resp.StatusCode != 200 {
        log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
    }
    // fmt.Println(string(bodyText))
    destructure(word, bodyText)
}

func main() {
    if len(os.Args) != 2 {
        fmt.Fprintf(os.Stderr, `usage: simpleDict WORD example: simpleDict hello`)
        os.Exit(1)
    }
    word := os.Args[1]
    index := 0
    // dictArr := []string{"caiyun", "youdao"}
    dictReq := []func(string) *http.Request{queryCaiYunDict, queryYouDaoDict}
    // destructureYouDao
    destructuring := []func(string, []byte){destructureCaiYun, destructureYouDao}
    query(word, dictReq[index], destructuring[index])
}

四、课后个人总结

  • go 的 switch 语句中,case 后面可以不用加 break,但是如果想要继续执行下一个 case,需要加 fallthrough
  • go 的 switch 十分灵活,在多分支的情况下可以用来代替 if-else,更加清晰
  • go 的函数最好写 return 语句来返回值,这样更清晰
  • go 一般返回两个值,一个是结果,一个是错误,如果错误不为空,说明有错误,如果为空,说明没有错误

五、引用参考