GO 学习笔记(三)

144 阅读5分钟

控制流程

go 语言的控制流程语句与 js 相似,需要注意的是语法上的不同。下面是控制流程写法的例子。

  • if-else 条件判断的使用
package main

import (
	"fmt"
	"os"
	"strconv"
)

func main() {
	if scoer, _ := strconv.Atoi(os.Args[1]); scoer < 60 {
		fmt.Println("bad")
	} else {
		fmt.Println("good")
	}
}

// go run .\06\switch\geometry.go 20
// bad

// go run .\06\switch\geometry.go 80
// good

os.Args 变量包含传递给程序的每个命令行参数。这些值的类型为 string。

  • for 循环的使用
package main

import ( "fmt" )

func main() {
    for i := 0; i < 10; i += 2 {
        fmt.Println(i)
    }
}

  • switch 的使用
package main

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

// 生成 max 以内的随机数
func randNum(max int32) int32 {
	sec := time.Now().Unix()
	rand.Seed(sec)
	return rand.Int31n(max)
}

func main() {
	num := randNum(10)

	switch num {
	case 1:
		fmt.Println("one...")
	case 2:
		fmt.Println("two...")
	default:
		fmt.Println("other", num)
	}

}

  • fallthrough 关键字
  • defer关键字

defer是Go语言中的延迟执行语句,用来添加函数结束时执行的代码,常用于释放某些已分配的资源、关闭数据库连接、断开socket连接、解锁一个加锁的资源。

下面例子是关于文件读写的例子,在完成文件操作后,关闭文件,释放资源。

package main

import (
	"fmt"
	"io"
	"os"
)

func main() {
    newfile, error := os.Create("learnGo.txt")
    if error != nil {
        fmt.Println("Error: Could not create file.")
        return
    }
    defer newfile.Close()

    if _, error = io.WriteString(newfile, "Learning Go!"); error != nil {
        fmt.Println("Error: Could not write to file.")
        return
    }

    newfile.Sync()
}

  • panic() panich函数使程序中断

特殊的数据结构

1、数组

练习 - 使用数组 - Learn | Microsoft Docs

  • 初始化时需定义长度和类型,不可改变长度
  • 会默认分配零值,不同类型的零值参考基本类型

定义数组:

  1. 基本

    var a [3]int
    a[1] = 1
    a[2] = 2
    fmt.Println(a[0], a[1], a[len(a)-1])
    
    // 0 1 2
    
  2. 赋初始值

    数组长度为3,但这里只给了两个初始值所以打印出的数据后面有个空格

    name := [3]string{"zhangsan", "lisi"}
    fmt.Println(name)
    
    //[zhangsan lisi ]
    
  3. 不指定长度,根据数据多少来确定数组长度

    可以看到这里的打印的结果中没有空格了,数组的长度是2

    name2 := [...]string{"zhangsan", "lisi"}
    fmt.Println(name2, len(name2))
    
    //[zhangsan lisi] 2
    
  4. 指定位置赋值

    定义一个没有指定长度的 string 类型的数组 name3,并在数组下标为 3 的地方赋值 codeniu,程序推断出数组长度为 4

    name3 := [...]string{3: "codeniu"}
    fmt.Println("first index:", name3[0])
    fmt.Println("last index:", name3[len(name3)-1])
    fmt.Println(name3, len(name3))
    
    //first index:
    //last index: codeniu
    //[   codeniu] 4
    

2、切片

练习 - 了解切片 - Learn | Microsoft Docs

  • 切片跟数组相同,但是与数组更重要的区别是切片的大小是动态
  • 可以使用相同的方式去声明切片和数组

切片的三要素

  1. 基础数组 array
  2. 切片的长度 len(s):切片中元素的个数
  3. 切片的容量 cap(s):切片开头与基础数组结束之间的元素数目

切片的语法:s[i:p]

  1. s 数组
  2. i 起始指针
  3. p 结束指针

切片可以理解为从一个数组上切下来一部分,数组 array := [12]int{1,2...12}, 切片 quarter1 := array[0:3], 从0 开始切直到下标为3时停止,quarter1 的元素为[1,2,3], 长度 len(quarter1) 是 3,cap(quarter1) 是 12。

容量为什么是 12 呢?原因是基础数组有更多元素或位置可供使用,但对切片而言不可见。可以这么理解,容量就是数组长度 - 切片的起始指针。

例如:quarter2 := array[3:6],长度为 3,容量为 9( len(array) - i ).

拓展切片:

package main

import "fmt"

func main() {
    months := []string{"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
    quarter2 := months[3:6]
    quarter2Extended := quarter2[:4]
    fmt.Println(quarter2, len(quarter2), cap(quarter2))
    fmt.Println(quarter2Extended, len(quarter2Extended), cap(quarter2Extended))
}

// 这个操作将切片 quarter2 的长度拓展为 4

删除项:

Go 没有内置函数用于从切片中删除元素。设计一个函数用于删除切片中指定下标的元素。内置函数 append(slice, element)便于你向切片添加元素。

package main

import "fmt"

func main() [] {
    letters := [...]int{1,2,3,4}
    remove = 2

    if remove < len(letters) {

        fmt.Println("Before", letters, "Remove ", letters[remove])

        letters = append(letters[:remove], letters[remove+1:]...)

        fmt.Println("After", letters)
    }
    
}

备份:

在切片的中更改元素会影响到原始数组

package main

import "fmt"

func main() {
	a := [...]int{1, 2, 3, 4}

	var slice1 = a[0:2]
	var slice2 = a[1:4]

	slice1[1] = 100

	fmt.Println(a)
	fmt.Println(slice1)
	fmt.Println(slice2)
}

输出:

[1 100 3 4] [1 100] [100 3 4]

创建副本后再更改可以消除影响

make() 内置函数 make([]type,length) 用于生成一个空切片

copy() 内置函数 copy(dst, src []Type) 用于创建切片的副本,参数分别为目标切片和原切片

package main

import "fmt"

func main() {
	a := [...]int{1, 2, 3, 4}
	
	var slice1 = a[0:2]
	slice2 := make([]int, 3)
	copy(slice2, a[1:4])

	slice1[1] = 100

	fmt.Println(a)
	fmt.Println(slice1)
	fmt.Println(slice2)
}

输出:

[1 100 3 4] [1 100] [2 3 4]

3、映射

练习 - 使用映射 - Learn | Microsoft Docs

如何理解 Go 中的映射?

是键值对的集合,映射中所有的键的类型必须相同,他们值得类型也必须相同,但是键与值可以是不同得类型。

声明和初始化:

map 关键字,map[T]T 第一个 T 表示键得类型,第二个 T 表示值得类型。

package main

import "fmt"

func main() {
    studentsAge := map[string]int{
        "john": 32,
        "bob":  31,
    }
    fmt.Println(studentsAge)
}

只声明不初始化:

var studentsAge map[string]int
// or
studentsAge := make(map[string]int)

需要注意得是,通过 (var studentsAge map[string]int) 这种方式创建得映射叫做 nil 映射,映射不能进行添加操作,否则会报错。

package main

import "fmt"

func main() {
    var studentsAge map[string]int
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    fmt.Println(studentsAge)
}

输出:

panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()
        /Users/johndoe/go/src/helloworld/main.go:7 +0x4f
exit status 2

此规则仅适用于添加项的情况。 如果在 nil 映射中运行查找、删除或循环操作,Go 不会执行 panic。

访问项:

Go 中映射得访问与 JS 中类似,访问映射中没有的项时 Go 不会返回错误。但有时需要知道某个项是否存在。 在 Go 中,映射的下标表示法可生成两个值。 第一个是项的值。 第二个是指示键是否存在的布尔型标志。

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31

    age, exist := studentsAge["christy"]
    if exist {
        fmt.Println("Christy's age is", age)
    } else {
        fmt.Println("Christy's age couldn't be found")
    }
}

删除项:

使用内置函数 delete() ,如果你尝试删除不存在的项,Go 不会执行 panic。

delete(studentsAge, "john")

映射中的循环:

使用rang 关键字,range 会首先生成项的键,然后再生成该项的值。 可使用 _ 变量忽略其中任何一个

package main

import (
    "fmt"
)

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    for name, age := range studentsAge {
        fmt.Printf("%s\t%d\n", name, age)
    }
}

本文正在参加技术专题18期-聊聊Go语言框架