GO语言笔记(五) | 青训营

54 阅读5分钟

多种赋值方式和限制

现在来说一下GO语言的赋值,多种多样应该说。

s := ""
var s string
var s = ""
var s string = ""

以上的几个都是等价的,但是却有不同的使用场景,我们需要根据使用的情况来选择。

具体来说,第一种形式,短变量声明,最简洁,但是只能用于函数内部使用,不能用于包变量。GO语言会自动识别具体变量类型。

第二种还是比较常见的,第三种配合同时创建多个变量的时候也很常见:

image-20230823230907209

第四种方式,一本书里面的原话是这样的,我觉得比我自己解释要好:

第四种形式显式地标明变量的类型,当变量类型与初值类型相同时,类型冗余,但如果两者类型不同,变量类型就必须了。

还有一种声明变量的方式:

var (
    a string
    b int
    c float64
)

GO语言是静态编译语言,强类型语言,如果显式地赋值的类型错误的时候,就会导致编译错误。并不像JavaScript等那样自动给你转换过去。

for循环的另外一种形式,在某种数据类型的部分区间,也就是range上遍历。

for _,arg :=range os.Args[1:]{
    //balabala
}

每一次循环,range都会产生一对值:分别是索引还有对应的元素值。有的时候,我们只是想要对应的元素值,索引不需要。但是它返回我们就要接收,这个时候可以使用下划线_来丢弃不需要的索引(其实就是字符串或者切片的下标)。

如果我们想要让一个切片里面的所有字符串当成一个字符串输出出来,可以使用strings包的Join函数。切片里面每一个元素都是一个字符串,Join函数接收两个参数,一个是字符串切片,另外一个是字符串间隔符,即每个字符串之间使用什么间隔开。

在GO当中存在字符串数组,但它相对来说并不灵活,初始化后长度大小固定。以下是两者的区别:

image-20230823234410860

确实如果不仔细看,还真的没啥区别。最主要的区别就是在于是否固定了数组的大小,如果确认了那就是数组,不过不确定那就是切片。

如果我们直接打印切片的话,它会被[]包裹在里面,规定如此。

这里我们提供三个书上的例题,如果你和我一样是新手,可以一起来做看看效果。

练习 1.1: 修改echo程序,使其能够打印os.Args[0],即被执行命令本身的名字。

image-20230824200803915

练习 1.2: 修改echo程序,使其打印每个参数的索引和值,每个一行。

image-20230824201143007

练习 1.3: 做实验测量潜在低效的版本和使用了strings.Join的版本的运行时间差异。(1.6节讲解了部分time包,11.4节展示了如何写标准测试程序,以得到系统性的性能评测。)

这个我不会......哈哈哈,以后再说。

我们继续往下面学习,在学习进行中,我发现了现在存放学习资源代码的地方,我们可以使用git来克隆到本地运行,我把总的根目录放在这里,方便各位查找:gopl

新知识

接下来我们继续来看一个代码,根据它引出来其他的知识:

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    counts := make(map[string]int)
    input := bufio.NewScanner(os.Stdin)
    for input.Scan() {
        counts[input.Text()]++
    }
    // NOTE: ignoring potential errors from input.Err()
    for line, n := range counts {
        if n > 1 {
            fmt.Printf("%d\t%s\n", n, line)
        }
    }
}
//代码来源于书籍。

里面用到了另外一个程序结构:分支结构(叫法不同)。

使用if关键词,后面的条件两边不加括号,主体添加{}else部分也是可选部分,可以不存在。

里面存在一个map,是一个键值存储的集合,对元素的集合提供一些(常数时间的)操作。意思就是基本上对其操作都是在固定时间内完成的,不会因为大小产生变化。

其中可以是任意可以用来==判断的数据类型,是任意类型都可。使用内置函数make来创建空map

代码当中的counts就是一个map,通过counts[input.Text()]++来进行对其中的一个键对应的值做加一操作。通过counts[键]的方式可以获得对应的值,然后自增一操作。

当map当中不存在某个键的值时,会将其对应的值设置为对应类型的零值。

我们可以使用基于range关键字的循环,但是map的循环并不遵循一定顺序,或者说顺序是随机的。

这种设计是有意为之的,因为能防止程序依赖特定遍历顺序,而这是无法保证的。(译注:具体可以参见这里stackoverflow.com/questions/1…

代码当中使用了bufio包,Scanner类型是该包最有用的特性之一,它读取输入并将其拆成行或单词;通常是处理行形式的输入最简单的方法。

程序使用短变量声明创建bufio.Scanner类型的变量input

对应到代码当中具体位置,就是说从系统标准输入当中读取输入,bufio.NewScanner(os.Stdin)会返回一个新的Scanner对象给input变量。每次调用 input.Scan() 时,它都会尝试从 os.Stdin 读取下一行,并移除行尾的换行符。你可以使用 input.Text() 来获取这行文本。如果 Scan 检测到输入已经结束(例如,因为用户按下了 Ctrl-D 或 Ctrl-Z),它将返回 false,这会结束循环。

我们就可以通过input.Err()函数来查看相关报错。