Go 语言入门指南:基础语法和常用特性解析(2)一些特性|青训营

41 阅读4分钟

在上一篇文章中,我已经介绍了Go的一些基本语法,了解这些已经可以用Go写出来一些基本的逻辑。然而Go相对于其他语言独具特色的是它的另外一些特性,比如Go的切片、函数的使用,还有最重要的特性——协程。

切片

在Go语言中,数组的长度在定义时就被确定,不可以再更改。而切片就是对数组的一种抽象,它实现了动态的数组,可以从数组中截取某一片段,也可以添加新的元素。切片的功能可以类比于Python中对于列表的一些切片操作。

切片的声明有两种形式,可以声明一个未定义长度的数组:

var identifier []type

也可以使用make()函数来创建一个切片:

slice1 := make([]type, length, capacity)

其中type是切片里元素的数据类型,length是切片的初始长度,这两者均为必须定义的参数。capacity是可选参数,定义切片的容量大小。

切片的截取类似于Python中列表的切片操作。众所周知,在Python中,对于一个列表,通过

list_B = list_A[lowerIndex:upperIndex]

的方式可以截取这两个下标之间的子列表。切片也是通过下标的方式进行子切片的截取:

s1 := s[startIndex:endIndex] 

此外对于切片,提供了append和copy函数,分别用于切片的添加元素与复制操作。 append函数的使用如下:

s = append(s, 1)
s = append(s, 2, 3, 4)

append可以作用于空的切片,可以添加一个元素,也可以添加多个元素。

copy函数的用法:

copy(b, a)

以上即是把切片a中的元素复制到切片b。

range

类似于Python中的range,Go语言中的range关键字作用于所有可以迭代的数据结构上,例如数组、切片、通道和集合。range通过在for循环中使用,来循环迭代上述数据结构。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。使用方法如下:

for key, value := range oldMap {
    newMap[key] = value
}

若不需要用到value,可以把value的定义省略。若不需要使用key,可以把key替换为空操作符_

Go中的函数

Go的函数结构如下:

func function_name( [parameter list] ) [return_types] {
      ...
}

在参数列表中,参数的定义均为“变量名 变量类型”的格式。若多个参数的类型是相同的,则可以以“变量a,变量b 变量类型”的格式声明。类似于C,Go的函数参数支持值传递和引用传递,前者传递变量中的内容,后者通过&符,传递变量的引用。

此外,Go中的函数支持返回多个值,类似于Python。返回的各个变量的数据类型要定义在函数声明后面。

协程(Goroutine)

协程是一种轻量级的线程,其在操作系统中通常被称为用户态线程,因为它们是由用户程序自己实现的,而不是由操作系统内核实现的。相比于多线程,每个协程只占用极少的栈空间和一些内存空间,占用资源少。因为只涉及用户态的栈切换和寄存器的保存与恢复,协程的切换成本低。

协程的实现是基于 Go 运行时系统,在程序启动时,Go 运行时系统会创建一个主协程,该协程负责程序的初始化和启动。在程序运行过程中,通过 go 关键字可以创建新的协程。

func CalSquare() {
	src := make(chan int)
	dest := make(chan int, 3)
        
        // 协程A 输入1-10
	go func() {
		defer close(src)
		for i := 0; i < 10; i++ {
			src <- i * 1
		}
	}()
        
        // 协程B 接收A的输出并计算平方值
	go func() {
		defer close(dest)
		for i := range src {
			dest <- i * i
		}
	}()
        
        // 主协程输出B的结果
	for i := range dest {
		println(i)
	}
}

以上程序定义了两个子协程。被go关键字修饰的函数,都实现了并发的运行。开头定义了src和dest两个通道,通道可以实现协程之间的通信。