Go 基础入门笔记 – Part 2
本文最后更新于 389 天前,其中的信息可能已经有所发展或是发生改变。

承接上文 Go 基础入门笔记 – Part 1 ,本文将介绍 Go 语言基础知识的后半部分:

  • 数组与切片
  • Map 及其进阶使用
  • 字符串与 Byte 类型
  • 函数

数组与切片

数组

数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。

func TestArrayInit(t *testing.T) {
	var arr [3]int               //Length set but not initialized.
	arr1 := [4]int{1, 2, 3, 4}   //Length set and initialized.
	arr3 := [...]int{1, 3, 4, 5} //Length not defined by user, set to 4 for defined integers.
	arr1[1] = 5                  //set value for variable in array.
	t.Log(arr[1], arr[2])
	t.Log(arr1, arr3)
}

在上面的例子中,可以看到声明数组的多个方式。

  • 在声明数组及其长度时,可以同时对其数值进行初始化。
  • 如果数组长度不确定,可以使用 [...] 代替数组的长度。
func TestArrayTraverse(t *testing.T) { //Shown in recent codes.
	arr := [...]int{1, 1, 4, 5, 1, 4}
	for i, e := range arr {
		t.Log(i, e)
	}
}

在上一篇文章提到,我们可以通过 for…range 语句方便的对数组进行遍历。

func TestArrayIntercept(t *testing.T) { //Array interception, usage: [<front>:<rear(not included)>]
	arr := [...]int{1, 1, 4, 5, 1, 4}
	arrSec := arr[0:]
	arrSec1 := arr[1:5]
	t.Log(arrSec)
	t.Log(arrSec1)
}

如上面的例子所示,我们可以截取数组中连续的一部分,用于给其他变量赋值,传递返回值等。

切片

Go 中提供了一种灵活,功能强悍的内置类型切片。

与数组相比,切片的长度是不固定的,可以追加元素。

func TestSliceInit(t *testing.T) {
	var s0 []int            //Declare new slice.
	t.Log(len(s0), cap(s0)) //Show length and capacity.
	s0 = append(s0, 1)      //Insert new data from tail.
	t.Log(len(s0), cap(s0)) //Show length and capacity.
	s1 := []int{1, 2, 3, 4} //Declare new slice and initialize.
	t.Log(len(s1), cap(s1)) //Show length and capacity.

	s2 := make([]int, 3, 5) //Another way to make new slice.
	t.Log(len(s2), cap(s2)) //Show length and capacity.
	//t.Log(s2[0], s2[1], s2[2], s2[3], s2[4])
	t.Log(s2) //Showing all data.
	s2 = append(s2, 1)
	s2 = append(s2, 4)
	t.Log(s2)               //Showing all data after append.
	t.Log(len(s2), cap(s2)) //Show length and capacity.
}

在上面的例子中,我们能看到声明切片的多种方式,并可以直观的返回其对应长度与容量。

func TestSliceGrowing(t *testing.T) { //Slice capacity doubles when there's not enough space.
	var growth []int
	for i := 1; i <= 16; i++ {
		growth = append(growth, i)
		t.Log(len(growth), cap(growth), growth)
	}
}

通过执行上述代码,我们可以发现:

  • 当切片的元素个数即将超过其容量时,其容量会扩增到原来的两倍,进行内存的分配。
func TestSliceMemoryShare(t *testing.T) { //Slice interceptions share their memory and data.
	var months = []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
	var Q2 = months[3:6]
	t.Log(Q2, len(Q2), cap(Q2))
	Q2[2] = "July"
	t.Log(months)
}

在上述代码,我们可以通过设置下限及上限来设置截取切片。

但截取切片后赋予新变量,其内存空间与原切片的内存共享,即会相互影响。

Map 及其进阶使用

Map 是一种无序的键值对的集合。

func TestMapInit(t *testing.T) { //Different ways to declare maps.
	m1 := map[int]int{1: 1, 2: 4, 3: 9}
	t.Log(m1[2], len(m1))
	m2 := map[int]int{}
	m2[4] = 16
	t.Log(m2, len(m2))
	m3 := make(map[int]int, 10)
	t.Log(len(m3))
}

在上面的例子中,我们能看到声明集合的多种方式。

func TestAccessKey(t *testing.T) { //Check if a key is valid.
	m1 := map[int]int{}
	t.Log(m1[1])
	m1[2] = 0
	t.Log(m1[2])
	if v, ok := m1[3]; ok {
		t.Log(v)
	} else {
		t.Log("Nil")
	}
}

在对map里面的元素进行调用时,若不存在相应的键值,仍会返回初始值(如 int 类型的 0)。

此时,通过对 key 的检验,可以排除该键值是否存在的问题。

func TestMapWithFunction(t *testing.T) { //Map implements factory mode.
	m := map[int]func(op int) int{}
	m[1] = func(op int) int { return op }
	m[2] = func(op int) int { return op * op }
	m[3] = func(op int) int { return op * op * op }
	t.Log(m[1](2), m[2](2), m[3](2))
}

在上述代码中,我们可以看到,可以通过使用函数值,实现map的工厂模式。

func TestMapToSet(t *testing.T) { //Map implements Set collection.
	m := map[int]bool{}
	m[1] = true
	t.Log(m[1], m[3], len(m))
	delete(m, 1)
	m[3], m[5] = true, true
	t.Log(m[1], m[3], len(m))
}

Go的内置集合中没有Set的实现,但是我们可以使用 map[type]bool 来去实现一个Set。

字符串与 Byte 类型

Go 当中的字符串本质是只读的字符型数组。

func TestString(t *testing.T) {
	str := "Hello world"
	t.Log(str, len(str))
	//str[6] = '_'	//compile error, can't assign
	str = "逸一时,误一世"
	t.Log("Sentence length:", len(str)) //length is 21, 3 bytes for a Chinese character
	str = "\xE4\xB8\x96"                //UTF-8 code
	t.Log("UTF8 word and length:", str, len(str))
}

这段代码可以这样理解:

  • 字符串本质是只读的字符型数组,无法通过类似数组中元素修改那样修改字符串。
  • 字符串可存储 Unicode 字符,文中的中文字符为 UTF-8 编码,每个字占用 3 字节。
func TestStringFunctions(t *testing.T) {
	str := "逸一时,误一世,逸久逸久罢已龄"
	strParts := strings.Split(str, ",")
	for _, e := range strParts {
		t.Log(e)
	}
	t.Log(strings.Join(strParts, "_"))

	str1 := strconv.Itoa(10) //Convert integer to string
	t.Log(str1)
	ret, err := strconv.Atoi(str1) //Convert string to integer, return integer and error code (if exist)
	if err != nil {
		t.Log("Convert failed")
	} else {
		t.Log(ret - 10)
	}
}

上述代码举例了一些常用的相关函数:

  • strings.Split 函数,用于将字符串通过特定关键字拆分为字符串“数组”。
  • strings.Join 函数,用于将字符串“数组”通过特定关键字重新组合为单个字符串。
  • strconv 函数,用于对相应的变量类型进行字符转换。

函数

函数是基本的代码块,用于执行一个任务。

这里直接放出样例:

func MultiValue() (int, int) {
	rand.Seed(time.Now().Unix())
	return rand.Intn(10), rand.Intn(100)
}

func VerySlowFunction(op int) int {
	i := 0
	rand.Seed(time.Now().Unix())
	for rand.Intn(100) != op {
		time.Sleep(1 * time.Millisecond)
		i++
	}
	return i
}

func spentTime(inner func(op int) int) func(testInt int) {
	return func(n int) {
		start := time.Now()                                          //Record start time.
		inner(n)                                                     //Execute inner function.
		fmt.Println("Time spent:", time.Since(start).Milliseconds()) //Calculate spent time.
	}
}

func IntSum(nums ...int) int { //multi parameters
	ans := 0
	for _, num := range nums {
		ans += num
	}
	return ans
}

func CleanOutput() {
	fmt.Println("Cleaning resources... Please wait")
}

func TestDefer(t *testing.T) {
	defer CleanOutput() //defer is always reachable after function execution.
	fmt.Println("Function start.")
	panic("Function execution failed.")
	fmt.Println("Unreachable printing.") //Println after panic, unreachable.
}

func TestFunctions(t *testing.T) {
	t.Log(MultiValue())             //Return multiple value
	spentTime(VerySlowFunction)(67) //Test function execution time.
	t.Log(IntSum(1, 2, 3, 4))       //Test summary function.
}

通过上述代码,可以知道:

  • MultiValue 函数中,Go 函数可以返回多个值。
  • spentTime 函数中,可以获知 Go 语言可以很灵活的创建函数,并作为另外一个函数的实参。
  • IntSum 函数中,可知函数可以接受可变数量的数值。
  • TestDefer 函数中,可知 defer 始终会在函数执行结束后执行,即使函数中途报错退出。
本站作品采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇