Go入门之基础与语言特性 | 青训营笔记

94 阅读5分钟

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

  • 本文所记录的是个人认为课程中比较重要的点的记录以及对课后作业的笔记

Golang - 特性

高性能、高并发

媲美C++和Java的性能和并发
Go的并发不像其他语言需要依托于Cloud实现(即不需要通过寻找性能高度优化的第三方库开发高并发app)

丰富的标准库

高度稳定性和兼容性
享受语言版本迭代带来的性能优化(这是第三方库所不具备的)

完善的工具链

编译、代码格式化、错误检查、包管理都有对应的工具 内聚了单元测试框架,支持单元测试,性能测试,性能优化等

静态链接

Go所有的编译结果都是静态链接的
部署方便快捷(支持直接运行一个可执行文件,不需要其他附属文件,线上容器镜像的体积能够控制的很小)

  • C++

    需附属大量.so文件才能运行

  • Java

    需附属庞大的Jre才能运行

快速编译

编译速度比C++快

跨平台

win / mac / linux / 路由 / 树莓派 等等

垃圾回收

无需考虑内存的分配与释放

Golang - 基础

array

声明数组的时候,我们可以选择指定数组长度,也可以选择交给编译器去为数组匹配合适长度
交给编译器处理数组长度需要用到 ... 进行表示,具体代码如下:

func main() {
	// 指定数组长度
	array_common := [6]int{}
	fmt.Printf("array_common.Length = %v\n", len(array_common))
	// 编译器自动匹配合适的长度
	array_compile := [...]int{1, 2, 3, 4}
	fmt.Printf("array_compile.Length = %v\n", len(array_compile))
}

程序输出:

array_common.Length = 6
array_compile.Length = 4

struct

Go 中的struct 类似于其他编程语言中的类,可以用struct定义一个结构,然后为这种结构类型附加方法,有面向对象那味,伪代码如下:

type Student struct {
        Sno   int
	Age   int
	Sex   int
        Name  string
	Hobby string
}

type Class struct {
	StuList []*Student
}

func (c *Class) StudentAdd(stu Student) error {
	return nil
}
func (c *Class) StudentRemove(stu Student) error {
	return nil
}
func (c *Class) StudentUpdate(stu Student) error {
	return nil
}

if

if 语句支持接收局部变量的判断,使得变量判断完后失效,即作用范围只在if的生效范围内,代码如下:

    if err := true; err != false {
            fmt.Printf("err:%#v", err) // err:true
    }
    fmt.Printf("err:%#v", err) // 报错,因为err作用范围只在if的生效范围之内

for

for range 一般应用在遍历以 k v 键值对为存储方式的结构类型上较多,典型的如下:

  • map
  • slice
    // map
    stuInfo := map[string]string{"name": "blue", "hobby": "golang"}
    for k, v := range stuInfo {
            fmt.Printf("key = %s, value = %s", k, v)
    }
    // slice
    mySlice := make([]int, 0)
    for _, val := range mySlice { // 省略第一个返回值是因为第一个返回值是下标,这里我们只想拿到 value
            fmt.Printf("value = %d", val)
    }

switch

switch 中 与其他编程语言不一样的是:

  • 默认情况下 case 自带 break
  • 需要执行下一个 case 需要使用 fallthrough 控制
func main() {
	switch {
	case 1 == 1+0:
		fmt.Println("1 == 1+0 Successful...")
		fallthrough
	case 2 == 1+1:
		fmt.Println("2 == 1+1 Successful...")
		fallthrough
	default:
		fmt.Println("default case")
	}
}

程序输出

1 == 1+0 Successful...
2 == 1+1 Successful...
default case

若将case1中的fallthrough注释, 程序输出如下:

1 == 1+0 Successful...

break、goto、return、panic

break、goto、return、panic一般作用于强制退出循环,其中goto、panic需谨慎使用
panic 得看情况使用,不能像break,return,goto那样比较随意
panic 相当于是抛出异常,程序会直接中断 代码如下:

func main() {
	stuInfo := map[string]string{"name": "blue", "hobby": "golang"}
	for k, v := range stuInfo {
		fmt.Printf("key = %s, value = %s\n", k, v)
		panic("do panic")
	}
	fmt.Printf("run here...")// 程序无法执行到这里
}

程序输出:

key = name, value = blue
panic: do panic

goroutine 1 [running]:
main.main()
        D:/main.go:11 +0x1ff
exit status 2

由此可见,panic虽然也能强制退出循环,但是是连带整个程序都退出了

strconv.Atoi()

其功能是将字符串类型的整数转换成int类型,必须保证是字符串类型的整数,对两种类型的参数做了示例,代码如下:

func main() {

	// 参数提供是字符串类型的整数
	str_number := "1008611"
	result_num, _ := strconv.Atoi(str_number)
	fmt.Printf("result_num.Type = %T , result_num.Value = %v\n", result_num, result_num)

	// 参数提供不是字符串类型的整数
	str_string := "abcd"
	result_str, _ := strconv.Atoi(str_string)
	fmt.Printf("result_str.Type = %T , result_str.Value = %v\n", result_str, result_str)
}

程序输出:

result_num.Type = int , result_num.Value = 1008611
result_str.Type = int , result_str.Value = 0

Day1 - 课后作业

1.修改第一个例子猜谜游戏里面的最终代码,使用fmt.Scanf来简化代码实现

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 input int
	for {
                // 使用fmt.Scanf()格式化输入
		fmt.Scanf("%d", &input)

		fmt.Println("You guess is", input)

		if input > secretNumber {
			fmt.Println("Your guess is bigger than the secret number. Please try again")
		} else if input < secretNumber {
			fmt.Println("Your guess is smaller than the secret number. Please try again")
		} else {
			fmt.Println("Correct, you Legend!")
			break
		}
	}
}

2.修改第二个例子命令行词典里面的最终代码,增加另一种翻译引擎的支持
伪代码如下,借助工具生成响应请求,相当于多出一种请求方式~
tool:
- 使用oktools.net/json2go 可以将JSON转Golang 结构体
- 使用curlconverter.com/go/ 可以将curl(bash)转Golang结构体
目前仅支持curl(bash),不支持curl(cmd)

// 生成彩云翻译的请求和响应结构体
type DictRequest struct {

}
type DictResponse struct {

}
// 生成某度翻译的请求和响应结构体
type BaiduRequest struct {
	
}
type BaiduResponse struct {

}
// 查询彩云api
func querycaiyunapi(word string) {

}
// 查询某度翻译api
func querybaiduapi(word string) {

}

func main() {
    // 使用彩云引擎
    querycaiyunapi(word)
    // 使用某度引擎
    querybaiduapi(word)
}

3.并行请求两个翻译引擎提高响应速度
基于上一步的伪代码中,我们只需要开启两个无缓冲channel和两个goroutine即可,优化后的结构:

func querybaiduapi(word string, c chan string) {
	var result string
	// 处理后将result发送到channel
	c <- result
}
func querycaiyunapi(word string, c chan string) {
	var result string
	// 处理后将result发送到channel
	c <- result
}
func main() {
	var word string
        
        // 定义两个channel
	caiyun := make(chan string)
	baidu := make(chan string)

        // 启动两个goroutine
	go querycaiyunapi(word, caiyun)
	go querybaiduapi(word, baidu)

        // 并行,有两个任务,因此循环总次数为2
	for i := 0; i < 2; i++ {
		select {
                // 接收到彩云api翻译的结果
		case transfer_caiyun := <-caiyun: 
			// do something
                        
                // 接收到某度api翻译的结果
		case transfer_baidu := <-baidu:
			// do something
		}
	}

}