您当前的位置:首页 > 计算机 > 编程开发 > Go语言

Go语言的基础代码解析

时间:09-02来源:作者:点击数:
CDSY,CDSY.XYZ

Go语言的基础代码解析

让我们首先来看看一些Go语言的代码。以下代码会从预定义的消息列表中随机选择一条,并打印到控制台:

package main

// 导入额外的功能包
import (
  "errors"
  "fmt"
  "log"
  "math/rand"
  "strconv"
  "time"
)

// 从https://en.wiktionary.org/wiki/Hello_World#Translations中摘取
var helloList = []string{
  "Hello, world",
  "Καλημέρα κόσμε",
  "こんにちは世界",
  "سلام دنیا‎",
  "Привет, мир",
}

main() 函数的定义如下:

func main() {
  // 使用当前时间为随机数生成器设置种子
  rand.Seed(time.Now().UnixNano())
  // 在列表范围内生成一个随机数
  index := rand.Intn(len(helloList))
  // 调用函数并接收多个返回值
  msg, err := hello(index)
  // 处理错误
  if err != nil {
    log.Fatal(err)
  }
  // 打印消息到控制台
  fmt.Println(msg)
}

接下来,我们来看看 hello() 函数的定义:

func hello(index int) (string, error) {
  if index < 0 || index >= len(helloList) {
    // 创建一个错误,将整数类型转换为字符串
    return "", errors.New("out of range: " + strconv.Itoa(index))
  }
  return helloList[index], nil
}

现在,我们逐步解析这段代码。

首先是脚本的开头部分:

package main

这是我们的包声明。所有Go语言文件必须以这种声明开始。如果你希望直接运行代码,需要将包命名为 main。如果不命名为 main,则可以将其用作库,并导入到其他Go代码中。创建一个可导入的包时,可以为它指定任意名称。所有位于同一目录下的Go文件都被认为是同一个包的一部分,这意味着所有文件必须具有相同的包名称。

接下来的代码部分是导入其他包的功能:

// 导入额外的功能包
import (
  "errors"
  "fmt"
  "log"
  "math/rand"
  "strconv"
  "time"
)

在这个例子中,所有导入的包都来自Go的标准库。Go的标准库质量很高且非常全面,强烈建议你充分利用它。如果一个包不是来自标准库,它会看起来像一个URL——例如,github.com/fatih/color

Go有一个模块系统,使得使用外部包变得简单。要使用新的模块,只需将其添加到导入路径中。下一次构建代码时,Go会自动下载这个模块。

导入仅适用于声明它们的文件,这意味着你必须在同一个包和项目中反复声明相同的导入。但是不用担心——许多工具和Go编辑器会自动为你添加和删除导入:

// 从https://en.wiktionary.org/wiki/Hello_World#Translations中摘取
var helloList = []string{
  "Hello, world",
  "Καλημέρα κόσμε",
  "こんにちは世界",
  "سلام دنیا‎",
  "Привет, мир",
}

在这里,我们声明了一个全局变量,它是一个字符串列表,并用数据进行初始化。Go中的字符串支持多字节的UTF-8编码,使其适用于任何语言。我们使用的列表类型叫做切片(slice)。Go中有三种列表类型:切片(slice)、数组(array)和映射(map)。这三种都是键值对集合,你可以使用键从集合中获取值。切片和数组使用数字作为键,第一个键总是0,并且这些数字是连续的,即没有中断。而在映射类型(map)中,你可以选择键的类型,用来根据其他数据查找映射中的值。

这里我们声明了一个函数。函数是一段在被调用时运行的代码。你可以将数据以一个或多个变量的形式传递给函数,并可选择性地从函数中接收一个或多个变量。Go中的 main() 函数是特殊的。main() 函数是Go代码的入口点。main 包中只能有一个 main() 函数。当你的代码运行时,Go会自动调用 main 来启动程序:

  // 使用当前时间为随机数生成器设置种子
  rand.Seed(time.Now().UnixNano())
  // 在列表范围内生成一个随机数
  index := rand.Intn(len(helloList))

在这段代码中,我们生成了一个随机数。首先,我们需要确保它是一个良好的随机数;为此,我们必须 设置种子(seed)。我们使用当前时间的Unix时间戳(以纳秒为单位)来设置种子。为了获取时间,我们调用了 time 包中的 Now 函数。Now 函数返回一个结构体类型的变量。结构体是属性和函数的集合,有点类似于其他语言中的对象。在这里,我们直接调用该结构体上的 UnixNano 函数。UnixNano 函数返回一个 int64 类型的变量,即64位整数,简单来说就是一个数字。这个数字被传递到 rand.Seed 函数中。rand.Seed 函数接受一个 int64 类型的变量作为输入。至此,我们已经成功地设置了随机数生成器的种子。

我们需要一个可以用来获取随机消息的数字。为此,我们使用 rand.Intn 函数。这个函数会返回一个介于 0 和你传入的数字之间的随机整数。虽然听起来有些奇怪,但这个方法非常适合我们的需求。这是因为我们的列表是一个切片(slice),其键从0开始,每个值递增1。这意味着最后一个索引比切片的长度少1。

为了说明这一点,以下是一些简单的代码:

package main

import (
  "fmt"
)

func main() {
  helloList := []string{
    "Hello, world",
    "Καλημέρα κόσμε",
    "こんにちは世界",
    "سلام دنیا‎",
    "Привет, мир",
  }
  fmt.Println(len(helloList))
  fmt.Println(helloList[len(helloList)-1])
  fmt.Println(helloList[len(helloList)])
}

这段代码会打印列表的长度,并使用该长度打印最后一个元素。为了做到这一点,我们必须减去1;否则,我们会得到一个错误,这也是最后一行代码造成的。

解释一下,len(helloList) 给出了切片的长度,索引从0开始,所以有效的索引范围是0到 len(helloList) - 1。访问超出此范围的索引会导致运行时错误。

一旦我们生成了随机数,就将其分配给一个变量。我们使用 := 这种简短的变量声明方式,这在Go语言中非常流行。这种语法告诉编译器将该值赋给变量,并隐式选择合适的类型。这个简写方式是Go语言让人感觉像动态类型语言的众多特性之一:

  // 调用函数并接收多个返回值
  msg, err := hello(index)

然后,我们使用这个变量来调用名为 hello 的函数。稍后我们会详细了解 hello 函数。需要注意的是,我们从函数中接收了两个值,并使用 := 语法将它们赋给两个新的变量,msg 和 err,其中 err 是第二个值:

func hello(index int) (string, error) {
…
}

这段代码是 hello 函数的定义;我们暂时不展示函数的具体实现。函数是一个逻辑单元,在需要时被调用。调用函数时,调用它的代码会暂停运行,直到函数执行完成。函数是组织和理解代码的一个好工具。在 hello 函数的签名中,我们定义了它接受一个 int 类型的参数,并返回一个 string 类型的值和一个 error 类型的值。在Go中,error 作为返回值的最后一项是很常见的。函数体在 {} 之间定义,以下代码是在函数被调用时执行的:

  if index < 0 || index >= len(helloList) {
    // 创建一个错误,将整数类型转换为字符串
    return "", errors.New("out of range: " + strconv.Itoa(index))
  }
  return helloList[index], nil

在这个函数体内,我们首先检查传入的索引是否在有效范围内。如果索引不在有效范围内,我们创建一个错误,并将整数转换为字符串,然后返回这个错误。否则,我们返回列表中对应索引的值以及 nil(表示没有错误)。

在这里,我们位于函数内部,函数体的第一行是一个 if 语句。if 语句在其 {} 之间的代码块会在布尔表达式为真时执行。布尔表达式是 if 和 { 之间的逻辑。在本例中,我们检查传入的 index 变量是否小于0或大于切片的最大可能索引值。

如果布尔表达式为真,我们的代码将返回一个空的 string 和一个 error 值。此时,函数的执行将停止,调用函数的代码将继续执行。如果布尔表达式不为真,if 语句中的代码将被跳过,函数将从 helloList 返回一个值,并返回 nil。在Go语言中,nil 表示没有值和没有类型:

  // 处理错误
  if err != nil {
    log.Fatal(err)
  }

在运行完 hello 函数后,首先需要检查它是否成功运行。我们可以通过检查存储在 err 中的 error 值来完成。如果 err 不等于 nil,则表示发生了错误。你会看到对 err 是否不等于 nil 的检查,而不是是否等于 nil 的检查,这样可以简化代码的逻辑。在发生错误的情况下,我们调用 log.Fatal,它会输出日志消息并终止应用程序。一旦应用程序被终止,就不会再执行任何代码:

  // 将消息打印到控制台
  fmt.Println(msg)

如果没有错误,则表示 hello 函数成功运行,msg 的值可以信赖并且有效。最后一步是通过终端将消息打印到屏幕上。

在这个简单的Go程序中,我们涵盖了许多关键概念,这些概念将在接下来的章节中详细探讨。

练习 1.01 – 使用变量、包和函数打印星号

在这个练习中,我们将利用之前示例中学到的一些知识,打印一个随机数量(1到5之间)的星号(*****)到控制台。这个练习将帮助你感受使用Go语言的工作方式,并练习一些我们在后续学习中会用到的Go语言特性。开始吧:

  1. 创建一个新文件夹,并在其中添加一个名为 main.go 的文件。
  2. 在 main.go 文件的顶部添加 main 包名:
    package main
    
  3. 在 main.go 文件中,添加我们需要的导入包:
    import (
      "fmt"
      "math/rand"
      "strings"
      "time"
    )
    
  4. 创建一个 main() 函数:
    func main() {
    
  5. 设置随机数生成器的种子:
      rand.Seed(time.Now().UnixNano())
    
  6. 生成一个介于 1 到 5 之间的随机数:
      r := rand.Intn(5) + 1
    
  7. 使用字符串重复功能生成一个包含所需数量星号的字符串:
      stars := strings.Repeat("*", r)
    
  8. 打印包含星号的字符串到控制台,并在末尾加上换行符,完成 main() 函数:
      fmt.Println(stars)
    }
    
  9. 保存文件。在新文件夹中,运行以下命令:
    go run .
    

在这个练习中,我们通过定义 main 包和其中的 main() 函数,创建了一个可运行的Go程序。我们通过导入标准库中的包来生成随机数、重复字符串并输出到控制台。

CDSY,CDSY.XYZ
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐