Go语言学习笔记(三)

教程:文档 - Go 编程语言 (studygolang.com)

调用模块代码

在call-module-code需要注意,需要在hello目录下操作

复制代码
go mod edit -replace example.com/greetings=../greetings

这是一个在Go项目的模块管理中的命令。在Go的模块管理工具(go mod)中,这个命令用于修改模块依赖关系。

具体来说,go mod edit -replace example.com/greetings=../greetings 这个命令的作用是:

  1. -replace: 这个选项表示你要替换一个已经存在的依赖。
  2. example.com/greetings: 这是你要替换的依赖的原始路径。
  3. ../greetings: 这是你要替换成的新路径。

所以,这个命令的意思是:将example.com/greetings这个依赖替换为项目根目录下的greetings目录。

简单来说,这个命令用于将一个远程依赖替换为本地路径的依赖。这在你想要使用本地版本的库而不是远程版本时非常有用,例如在开发过程中。

go mod tidy

go mod tidy 是一个 Go 语言的命令,用于自动添加或删除不必要的模块依赖项,使 go.mod 文件保持最新。

当你使用 Go 语言开发一个项目时,可能会使用 go get 命令来获取外部的依赖库。这些库会被添加到项目的 go.mod 文件中。随着时间的推移,有些依赖可能不再需要,或者新的依赖可能被添加进来。为了保持 go.mod 文件的整洁和一致性,你可以使用 go mod tidy 命令来自动处理这些不必要的依赖。

具体来说,go mod tidy 会做以下几件事:

  1. 删除不必要的依赖 :如果一个依赖在代码中没有被使用,或者其版本与 go.mod 文件中指定的版本不匹配,那么这个依赖会被从 go.mod 文件中移除。
  2. 添加缺失的依赖 :如果代码中使用了某个外部库,但这个库还没有被添加到 go.mod 文件中,那么 go mod tidy 会自动将其添加进来。
  3. 更新依赖的版本 :如果某个依赖有新版本可用,并且这个新版本与 go.mod 文件中指定的版本不匹配,那么 go mod tidy 会自动更新这个依赖的版本。

总之,go mod tidy 是一个非常有用的命令,可以帮助开发者自动管理项目的模块依赖,确保 go.mod 文件始终是最新的。

操作后hello文件夹下go.mod内容为

如果不操作,则报错hello.go:6:2: no required module provides package example.com/greetings; to add it:

go get example.com/greetings

处理错误

Go 复制代码
package greetings

import (
	"errors"
	"fmt"
)

// Hello returns a greeting for the named person.
func Hello(name string) (string, error) {
	if name == "" {
		return "", errors.New("empty name")
	}
	// Return a greeting that embeds the name in a message.
	message := fmt.Sprintf("Hi, %v. Welcome!", name)
	return message, nil
}
Go 复制代码
package main

import (
	"fmt"
	"log"

	"example.com/greetings"
)

func main() {
	// Get a greeting message and print it.
	message, err := greetings.Hello("")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(message)
}

使用日志

Go 复制代码
package main

import (
	"fmt"
	"log"

	"example.com/greetings"
)

func main() {
	log.SetPrefix("greetings: ")
	log.SetFlags(0)
	// Get a greeting message and print it.
	message, err := greetings.Hello("")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(message)
}

返回随机问候语

Go 复制代码
package main

import (
	"errors"
	"fmt"
	"log"
	"math/rand"
	// "example.com/greetings"
)

func Hello(name string) (string, error) {
	if name == "" {
		return name, errors.New("Empty name")
	}
	message := fmt.Sprintf(randomFormat(), name)
	return message, nil
}
func randomFormat() string {
	stringArray := []string{"Hi, %v. Welcome!",
		"Great to see you, %v!",
		"Hail, %v! Well met!"}
	index := rand.Intn(len(stringArray))
	return stringArray[index]
}
func main() {

	message, err := Hello("小明")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(message)
}

向多人问候

Go 复制代码
package greetings

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

func Hello(name string) (string, error) {
	if name == "" {
		return name, errors.New("Empty name")
	}
	message := fmt.Sprintf(randomFormat(), name)
	return message, nil
}
func randomFormat() string {
	stringArray := []string{"Hi, %v. Welcome!",
		"Great to see you, %v!",
		"Hail, %v! Well met!"}
	index := rand.Intn(len(stringArray))
	return stringArray[index]
}
func Hellos(names []string) (map[string]string, error) {
	cMap := make(map[string]string)
	for _, value := range names {
		message, err := Hello(value)
		if err != nil {
			return nil, err
		}
		cMap[value] = message
	}
	return cMap, nil

}

知识点:

1、函数入参为数组怎么写。

2、函数返回值为map怎么写

3、如何创建一个map make(map[string]string)

Go 复制代码
package main

import (
	"fmt"
	"log"

	"example.com/greetings"
	// "example.com/greetings"
)

func main() {
	names := []string{
		"小明",
		"小王",
	}
	message, err := greetings.Hellos(names)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(message)
}

添加单元测试

Go 复制代码
package greetings

import (
	"regexp"
	"testing"
)

func TestHelloName(t *testing.T) {
	name := "Gladys"
	want := regexp.MustCompile(`\b` + name + `\b`)
	msg, err := Hello("Gladys")
	if !want.MatchString(msg) || err != nil {
		t.Fatalf(`Hello("Gladys")=%q,%v,want match for %#q,nil`, msg, err, want)

	}

}
func TestHelloEmpty(t *testing.T) {
	msg, err := Hello("")
	if msg != "" || err == nil {
		t.Fatalf(`Hello("") = %q, %v, want "", error`, msg, err)
	}

}

名为TestHelloName的测试函数,它接受一个类型为*testing.T的参数t。这是Go语言中用于编写测试的标准方式。

TestHelloName方法若msg中不存在name或者err不为nil,则用例失败,

在vscode中,可以在测试函数前点击运行按钮运行,也可以在命令行运行go test或者 go test -v

编译和安装应用程序

代码编译为可执行文件,生成hello.exe

复制代码
go build

命令行下运行hello.exe

安装软件包

go install

A Tour of Go

这个tour教程不错。

左边为教程,右边为实例

指针

Go 拥有指针。指针保存了值的内存地址。

类型 *T 是指向 T 类型值的指针。其零值为 nil

复制代码
var p *int

& 操作符会生成一个指向其操作数的指针。

复制代码
i := 42
p = &i

* 操作符表示指针指向的底层值。

复制代码
fmt.Println(*p) // 通过指针 p 读取 i
*p = 21         // 通过指针 p 设置 i

这也就是通常所说的"间接引用"或"重定向"。

与 C 不同,Go 没有指针运算。

Go 复制代码
package main

import "fmt"

func main() {
	i, j := 42, 2701

	p := &i         // 指向 i
  fmt.Println(p)
	fmt.Println(*p) // 通过指针读取 i 的值
	*p = 21         // 通过指针设置 i 的值
	fmt.Println(i)  // 查看 i 的值

	p = &j         // 指向 j
	*p = *p / 37   // 通过指针对 j 进行除法运算
	fmt.Println(j) // 查看 j 的值
}

结构体

一个结构体(struct)就是一组字段(field)。

Go 复制代码
package main

import "fmt"

type Vertex struct {
	X int
	Y int
}

func main() {
	fmt.Println(Vertex{1, 2})
}

结构体字段

结构体字段使用点号来访问。

Go 复制代码
package main

import "fmt"

type Vertex struct {
	X int
	Y int
}

func main() {
	v := Vertex{1, 2}
	v.X = 4
	fmt.Println(v.X)
}

结构体指针

结构体字段可以通过结构体指针来访问。

如果我们有一个指向结构体的指针 p,那么可以通过 (*p).X 来访问其字段 X。不过这么写太啰嗦了,所以语言也允许我们使用隐式间接引用,直接写 p.X 就可以。

Go 复制代码
package main

import "fmt"

type Vertex struct {
	X int
	Y int
}

func main() {
	v := Vertex{1, 2}
	p := &v
	p.X = 1e9 //赋值 或者 (*p).X = 1e9 两种写法一样
	fmt.Println(v)
}

输出

复制代码
{1000000000 2}

结构体文法

结构体文法通过直接列出字段的值来新分配一个结构体。

使用 Name: 语法可以仅列出部分字段。(字段名的顺序无关。)

特殊的前缀 & 返回一个指向结构体的指针。

Go 复制代码
package main

import "fmt"

type Vertex struct {
	X, Y int
}

var (
	v1 = Vertex{1, 2}  // 创建一个 Vertex 类型的结构体
	v2 = Vertex{X: 1}  // Y:0 被隐式地赋予
	v3 = Vertex{}      // X:0 Y:0
	p  = &Vertex{1, 2} // 创建一个 *Vertex 类型的结构体(指针)
)

func main() {
	fmt.Println(v1, p, v2, v3)
}

数组

类型 [n]T 表示拥有 nT 类型的值的数组。

表达式

复制代码
var a [10]int

会将变量 a 声明为拥有 10 个整数的数组。

数组的长度是其类型的一部分,因此数组不能改变大小。这看起来是个限制,不过没关系,Go 提供了更加便利的方式来使用数组。

Go 复制代码
package main

import "fmt"

func main() {
	var a [2]string
	a[0] = "Hello"
	a[1] = "World"
	fmt.Println(a[0], a[1])
	fmt.Println(a)

	primes := [6]int{2, 3, 5, 7, 11, 13}
	fmt.Println(primes)
}

切片

每个数组的大小都是固定的。而切片则为数组元素提供动态大小的、灵活的视角。在实践中,切片比数组更常用。

类型 []T 表示一个元素类型为 T 的切片。

切片通过两个下标来界定,即一个上界和一个下界,二者以冒号分隔:

复制代码
a[low : high]

它会选择一个半开区间,包括第一个元素,但排除最后一个元素。

以下表达式创建了一个切片,它包含 a 中下标从 1 到 3 的元素:

复制代码
a[1:4]
Go 复制代码
package main

import "fmt"

func main() {
	primes := [6]int{2, 3, 5, 7, 11, 13}

	var s []int = primes[1:4]
	fmt.Println(s)
}

切片就像数组的引用

切片并不存储任何数据,它只是描述了底层数组中的一段。

更改切片的元素会修改其底层数组中对应的元素。

与它共享底层数组的切片都会观测到这些修改。

Go 复制代码
package main

import "fmt"

func main() {
	names := [4]string{
		"John",
		"Paul",
		"George",
		"Ringo",
	}
	fmt.Println(names)

	a := names[0:2]
	b := names[1:3]
	fmt.Println(a, b)

	b[0] = "XXX"
	fmt.Println(a, b)
	fmt.Println(names)
}

修改b的第一个值,切片a和数组中该值都被更改。

切片文法

切片文法类似于没有长度的数组文法。

这是一个数组文法:

复制代码
[3]bool{true, true, false}

下面这样则会创建一个和上面相同的数组,然后构建一个引用了它的切片:

复制代码
[]bool{true, true, false}
Go 复制代码
package main

import "fmt"

func main() {
	q := []int{2, 3, 5, 7, 11, 13}
	fmt.Println(q)

	r := []bool{true, false, true, true, false, true}
	fmt.Println(r)

	s := []struct {
		i int
		b bool
	}{
		{2, true},
		{3, false},
		{5, true},
		{7, true},
		{11, false},
		{13, true},
	}
	fmt.Println(s)
}

切片的默认行为

在进行切片时,你可以利用它的默认行为来忽略上下界。

切片下界的默认值为 0,上界则是该切片的长度。

对于数组

复制代码
var a [10]int

来说,以下切片是等价的:

复制代码
a[0:10]
a[:10]
a[0:]
a[:]
Go 复制代码
package main

import "fmt"

func main() {
	s := []int{2, 3, 5, 7, 11, 13}

	s = s[1:4]
	fmt.Println(s)

	s = s[:2]
	fmt.Println(s)

	s = s[1:]
	fmt.Println(s)
}

切片的长度与容量

切片拥有 长度容量

切片的长度就是它所包含的元素个数。

切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。

切片 s 的长度和容量可通过表达式 len(s)cap(s) 来获取。

你可以通过重新切片来扩展一个切片,给它提供足够的容量。试着修改示例程序中的切片操作,向外扩展它的容量,看看会发生什么。

Go 复制代码
package main

import "fmt"

func main() {
	s := []int{2, 3, 5, 7, 11, 13}
	printSlice(s)

	// 截取切片使其长度为 0
	s = s[:0]
	printSlice(s)

	// 拓展其长度
	s = s[:4]
	printSlice(s)

	// 舍弃前两个值
	s = s[2:]
	printSlice(s)
}

func printSlice(s []int) {
	fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
复制代码
len=6 cap=6 [2 3 5 7 11 13]
len=0 cap=6 []
len=4 cap=6 [2 3 5 7]
len=2 cap=4 [5 7]

nil 切片

切片的零值是 nil

nil 切片的长度和容量为 0 且没有底层数组。

Go 复制代码
package main

import "fmt"

func main() {
	var s []int
	fmt.Println(s, len(s), cap(s))
	if s == nil {
		fmt.Println("nil!")
	}
}
复制代码
[] 0 0
nil!

Go 语言之旅 (go-zh.org)

相关推荐
南宫生3 分钟前
力扣-数据结构-3【算法学习day.74】
java·数据结构·学习·算法·leetcode
挥剑决浮云 -8 分钟前
STM32学习之 按键/光敏电阻 控制 LED/蜂鸣器
c语言·经验分享·stm32·单片机·嵌入式硬件·学习
keep-learner21 分钟前
Unity Dots理论学习-2.ECS有关的模块(1)
学习·unity·游戏引擎
A懿轩A41 分钟前
C/C++ 数据结构与算法【树和二叉树】 树和二叉树,二叉树先中后序遍历详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·二叉树·
胡楚昊1 小时前
攻防世界PWN刷题笔记(引导模式)1-3
笔记
虾球xz1 小时前
游戏引擎学习第62天
学习·游戏引擎
上等猿1 小时前
Ajax笔记
前端·笔记·ajax
lmxnsI2 小时前
docker使用笔记
笔记·docker·容器
程序猿online2 小时前
nvm安装使用,控制node版本
开发语言·前端·学习
lijiachang0307182 小时前
设计模式(一):单例模式
c++·笔记·学习·程序人生·单例模式·设计模式·大学生