MENU

Catalog

Go 入门小册

April 29, 2024 • WIKI

Go 的 Hello World 实践

Go的运行环境安装参考 Install Go

使用任意文本编辑器或者IDE编写代码如下,文件名为 hello-world.go :

package main
import ("fmt")

func main() {
  fmt.Println("Hello World!")
}

使用 go run <程序文件> 运行 go 程序:

$ go run hello-world.go
hello world

使用 go build <程序文件> 编译 go 程序为二进制文件:

$ go build hello-world.go
$ ls
hello-world    hello-world.go

执行编译的生成的二进制文件 hello-world ,效果和go run 一样:

$ ./hello-world
hello world

Go 语法

Go文件由以下部分组成:

  • 包声明(Package declaration)
  • 导入包(Import packages)
  • 函数(Functions)
  • 语句和表达式(Statements and expressions)

看看如下示例代码,以便更好的理解:

package main
import ("fmt")

func main() {
  fmt.Println("Hello World!")
}

示例说明:

  • 第一行:在Go语言里,所有的程序都是包的一部分。这里使用 package 关键字定义这个程序属于哪个包。这里定义程序归属 main 包;
  • 第二行:import ("fmt") 导入 fmt 包中的所有文件;
  • 第三行:一行空行,Go语言会忽略空白,使用空白内容增加程序的可读性;
  • 第四行:func main() {} 是一个函数。其花括号{}内的任何代码都将被执行。
  • 第五行:fmt.Println() 是fmt包中提供的一个函数,它用于输出/打印文本。在这个示例中,它将输出“Hello World!”。
Note: In Go, any executable code belongs to the main package.

Go 语句

fmt.Println("Hello World!") 是一个语句。

在 Go 语言中,语句以行结尾(按Enter键)或分号“;”分隔。

按Enter键会隐式地将“;”添加到行的末尾(不会显示在源代码中)。

左花括号{不能出现在一行的开头。

比如执行如下代码,会报错 missing function bodysyntax error: unexpected semicolon or newline before {

package main
import ("fmt")

func main() 
// { 在行开头会报错
{
  fmt.Println("Hello World!")
}

Go 代码压缩

Go 语言允许编写更紧凑的代码,如下所示(但不推荐,会让代码难以阅读):

package main; import ("fmt"); func main() { fmt.Println("Hello World!");}

Go 注释

注释是在执行时被忽略的文本。注释可以用来解释代码,并使其更具可读性。在测试替代代码时,注释也可以用于阻止代码执行。Go支持单行或多行注释。

单行注释

单行注释以两个正斜杠(//)开头,编译器将忽略//和行尾之间的任何文本(不会执行)。

// This is a comment
package main
import ("fmt")

func main() {
  // This is a comment
  fmt.Println("Hello World!") // This is a comment
  // fmt.Println("This line does not execute")
}

多行注释

多行注释以/*开头,以*/结尾。编译器将忽略//之间的任何文本:

package main
import ("fmt")

func main() {
  /* The code below will print Hello World
  to the screen, and it is amazing */
  fmt.Println("Hello World!")
}

Go 变量

变量是用于存储数据值的容器。

Go 的变量类型

在 Go 语言中,有多种变量类型,比如:

  • int - 存储整数,比如 123 或 -123
  • float32 - 存储带小数的浮点数,比如 19.99 或 -19.99
  • string - 存储文本,比如 “Hello World”。字符串值需用双引号括起来
  • bool - 存储两个状态值, true 或者 false

更多变量类型在数据类型章节深入说明。

变量命名规则

变量可以有一个简短的名称(如x和y)或一个更具描述性的名称(年龄、价格、carname等)。

Go变量命名规则:

  • 变量名必须以字母或下划线字符(_)开头
  • 变量名不能以数字开头
  • 变量名只能包含字母数字字符和下划线(A-z、A-z0-9_
  • 变量名称区分大小写(age、Age和AGE是三个不同的变量)
  • 变量名的长度没有限制
  • 变量名不能包含空格
  • 变量名不能是任何Go关键字

多单词变量名

包含多个单词的变量名比较难阅读。

有几种方法用来提高它们的可读性:

驼峰命名法

除第一个单词外,每个单词都以大写字母开头:

myVariableName = "John"

帕斯卡命名法

每个单词都以一个大写字母开头:

MyVariableName = "John"

蛇式命名法

每个单词由一个下划线字符分隔:

my_variable_name = "John"

声明(创建)变量

有两种方式声明变量

  1. 一种是使用 var 关键字,其后跟变量名称和类型,语法如下:
var variablename type = value

注意: type 和 value 至少需指定一个

  1. 另一种是使用 := 标识符,其后跟变量值,语法如下:
variablename := value

这种声明方式下,变量的类型是根据值推断的,由编译器根据值觉得变量类型。

使用这种声明方式,变量的值是必须指定的。

带初始值的变量声明

如果变量的值从一开始就已知,则可以声明该变量并在一行中为其赋值:

package main
import ("fmt")

func main() {
  var student1 string = "John" //type is string
  var student2 = "Jane" //type is inferred
  x := 2 //type is inferred

  fmt.Println(student1)
  fmt.Println(student2)
  fmt.Println(x)
}

不带初始值的变量声明

在Go中,所有变量都需要被初始化。因此,如果您声明一个没有初始值的变量,其值将设置为其类型的默认值:

package main
import ("fmt")

func main() {
  var a string // 默认 ""
  var b int // 默认 0
  var c bool // 默认 false

  fmt.Println(a)
  fmt.Println(b)
  fmt.Println(c)
}

在声明后赋值

可以在变量声明后将值赋给该变量。这对于值最初未知的情况下是有帮助的。

package main
import ("fmt")

func main() {
  var student1 string
  student1 = "John"
  fmt.Println(student1)
}

var 和 := 的区别

var:=
可以在函数外部内部使用只能在函数内部使用
变量的声明和赋值可以分开变量的声明和赋值不可以分开(需要在一行)

多个变量声明

在Go中,允许在同一行中声明多个变量。

package main
import ("fmt")

func main() {
  var a, b, c, d int = 1, 3, 5, 7

  fmt.Println(a)
  fmt.Println(b)
  fmt.Println(c)
  fmt.Println(d)
}

注意:如果使用type关键字,则每行只能声明一种类型的变量。

如果未指定type关键字,则可以在同一行中声明不同类型的变量:

package main
import ("fmt")

func main() {
  var a, b = 6, "Hello"
  c, d := 7, "World!"

  fmt.Println(a)
  fmt.Println(b)
  fmt.Println(c)
  fmt.Println(d)
}

在块中声明变量

为了提高可读性,还可以将多个变量声明分组到一个块中:

package main
import ("fmt")

func main() {
   var (
     a int
     b int = 1
     c string = "hello"
   )

  fmt.Println(a)
  fmt.Println(b)
  fmt.Println(c)
}

如果一个变量应该具备一个无法更改的固定值,那么可以使用 const 关键字。

const 关键字将变量声明为“常数”,表明这个变量是只读的,不可更改的。

语法如下:

const CONSTNAME type = value

很显然,常量的声明中,赋值是必须的。

常量声明

以下是在Go中声明常量的示例:

package main
import ("fmt")

const PI = 3.14

func main() {
  fmt.Println(PI)
}

常量规则

  • 常量名称遵循与变量相同的命名规则
  • 常量名称通常用大写字母书写(便于识别和区分变量)
  • 常量可以在函数内部和外部声明

常量类型

有两种类型的常量:

  • 类型常量(Typed constants)
  • 无类型常量(Untyped constants)

类型常量(Typed constants)

声明常量时指定类型,常量即为类型常量,类型常量的类型就是指定的类型:

package main
import ("fmt")

const A int = 1

func main() {
  fmt.Println(A)
}

无类型常量(Untyped constants)

声明常量时不指定类型,常量即为无类型常量,无类型常量的类型由编译器通过其值推断:

package main
import ("fmt")

const A = 1

func main() {
  fmt.Println(A)
}

常量的只读性和不可更改性

声明一个常量后,将不能更改其值,否则会报错:

package main
import ("fmt")

func main() {
  const A = 1
  A = 2
  fmt.Println(A)
}
// 报错:./prog.go:8:7: cannot assign to A

多个常量声明

为了便于阅读,可以将多个常量分组为一个块进行声明:

package main
import ("fmt")

const (
  A int = 1
  B = 3.14
  C = "Hi!"
)

func main() {
  fmt.Println(A)
  fmt.Println(B)
  fmt.Println(C)
}

Go 输出函数

Go 语言中有三个函数用于输出文本:

  • Print()
  • Println()
  • Printf()

Print() 函数

Print() 函数使用默认格式打印参数。

package main
import ("fmt")

func main() {
  var i,j string = "Hello","World"

  fmt.Print(i)
  fmt.Print(j)
}
// Result:
// HelloWorld

如果我们想在新行中打印参数,我们需要使用\n

package main
import ("fmt")

func main() {
  var i,j string = "Hello","World"

  fmt.Print(i, "\n")
  fmt.Print(j, "\n")
}
// Result:
// Hello
// World

也可以只使用一个Print()来打印多个变量。

package main
import ("fmt")

func main() {
  var i,j string = "Hello","World"

  fmt.Print(i, "\n",j)
}
// Result:
// Hello
// World

如果两个参数都不是字符串,则Print()在它们之间插入一个空格:

package main
import ("fmt")

func main() {
  var i,j = 10,20

  fmt.Print(i,j)
}
// Result:
// 10 20

Println() 函数

Println() 函数与 Print() 类似,不同之处在于在参数之间添加了一个空格,并在末尾添加了一行换行符:

package main
import ("fmt")

func main() {
  var i,j string = "Hello","World"

  fmt.Println(i,j)
}
// Result:
// Hello World

Printf() 函数

函数的 Printf() 首先根据给定的格式化谓词格式化参数,然后打印它们。

这里我们将使用两个格式化动词:

  • %v 用于打印参数的值
  • %T 用于打印参数的类型
package main
import ("fmt")

func main() {
  var i string = "Hello"
  var j int = 15

  fmt.Printf("i has value: %v and type: %T\n", i, i)
  fmt.Printf("j has value: %v and type: %T", j, j)
}
// Result:
// i has value: Hello and type: string
// j has value: 15 and type: int

格式化谓词 (formatting verbs)

Go 提供了几个可与 Printf() 函数一起使用的格式化谓词。

通用格式化谓词

如下格式化谓词适用于所有的数据类型:

VerbDescription
%v以默认格式打印值
%#v以 Go 语法格式打印值
%T打印值的类型
%%打印 % 符号

示例如下:

package main
import ("fmt")

func main() {
  var i = 15.5
  var txt = "Hello World!"

  fmt.Printf("%v\n", i)
  fmt.Printf("%#v\n", i)
  fmt.Printf("%v%%\n", i)
  fmt.Printf("%T\n", i)

  fmt.Printf("%v\n", txt)
  fmt.Printf("%#v\n", txt)
  fmt.Printf("%T\n", txt)
}
/*
Result:
15.5
15.5
15.5%
float64
Hello World!
"Hello World!"
string
*/

整数格式化谓词

以下谓词可以与整数数据类型一起使用:

VerbDescription
%b二进制输
%d十进制输出
%+d十进制输出且总是显示符号
%o八进制
%O以0o开头显示的八进制
%x十六进制, 小写字母显示
%X十六进制, 大写字母显示
%#x以0x开头显示的十六进制
%4d填充空格(宽度4,右对齐)
%-4d填充空格(宽度4,左对齐)
%04d填充零(宽度4)

示例如下:

package main
import ("fmt")

func main() {
  var i = 15
 
  fmt.Printf("%b\n", i)
  fmt.Printf("%d\n", i)
  fmt.Printf("%+d\n", i)
  fmt.Printf("%o\n", i)
  fmt.Printf("%O\n", i)
  fmt.Printf("%x\n", i)
  fmt.Printf("%X\n", i)
  fmt.Printf("%#x\n", i)
  fmt.Printf("%4d\n", i)
  fmt.Printf("%-4d\n", i)
  fmt.Printf("%04d\n", i)
}
/*
Result:
1111
15
+15
17
0o17
f
F
0xf
  15
15
0015

*/

字符串格式化谓词

以下谓词可以与字符串数据类型一起使用:

VerbDescription
%s将值打印为纯字符串
%q将值打印为双引号括住的字符串
%8s将值打印为纯字符串(宽度8,右对齐)
%-8s将值打印为纯字符串(宽度8,左对齐)
%x将值打印为byte值的十六进制转储
% x将值打印为带空格的十六进制转储

示例如下:

package main
import ("fmt")

func main() {
  var txt = "Hello"
 
  fmt.Printf("%s\n", txt)
  fmt.Printf("%q\n", txt)
  fmt.Printf("%8s\n", txt)
  fmt.Printf("%-8s\n", txt)
  fmt.Printf("%x\n", txt)
  fmt.Printf("% x\n", txt)
}
/*
Result:
Hello
"Hello"
   Hello
Hello
48656c6c6f
48 65 6c 6c 6f
*/

布尔格式化谓词

以下谓词可以与布尔数据类型一起使用:

VerbDescription
%t布尔运算符的值,格式为 true 或 false(与 %v 相同)

示例如下:

package main
import ("fmt")

func main() {
  var i = true
  var j = false

  fmt.Printf("%t\n", i)
  fmt.Printf("%t\n", j)
}
/*
Result:
true
false
*/

浮点值格式化谓词

以下谓词可以与浮点数据类型一起使用:

VerbDescription
%e以“e”为指数的科学记数法
%f小数形式,无指数
%.2f默认宽度,精度2
%6.2f宽度6,精度2
%g根据需要使用指数,仅使用必要的数字

示例如下:

package main
import ("fmt")

func main() {
  var i = 3.141

  fmt.Printf("%e\n", i)
  fmt.Printf("%f\n", i)
  fmt.Printf("%.2f\n", i)
  fmt.Printf("%6.2f\n", i)
  fmt.Printf("%g\n", i)
}

/*
Result:
3.141000e+00
3.141000
3.14
  3.14
3.141
*/

GO 基本数据类型

数据类型是编程中的一个重要概念。数据类型指定变量值的大小和类型。

Go是静态类型的,这意味着一旦定义了变量类型,它就只能存储该类型的数据。

Go有三种基本数据类型:

  • bool: 表示布尔值,为 true 或 false
  • Numeric: 表示整数类型、浮点值和复数
  • string: 表示字符串值

布尔

布尔数据类型是用 bool 关键字声明的,并且只能取 true 或 false值。
布尔数据类型的默认值为 false。

此示例显示了声明布尔变量的一些不同方法:

package main
import ("fmt")

func main() {
  var b1 bool = true // typed declaration with initial value
  var b2 = true // untyped declaration with initial value
  var b3 bool // typed declaration without initial value
  b4 := true // untyped declaration with initial value

  fmt.Println(b1) // Returns true
  fmt.Println(b2) // Returns true
  fmt.Println(b3) // Returns false
  fmt.Println(b4) // Returns true
}

整数

整数数据类型用于存储整数,如35、-50或1345000。

整数数据类型有两类:

  • 有符号整数 - 可以存储正值和负值
  • 无符号整数 - 只能存储非负值

Tip: 整数的默认类型为 int. 如果不指定类型, 默认类型会是 int

有符号整数

int 关键字声明有符号整数,可以存储正值和负值:

package main
import ("fmt")

func main() {
  var x int = 500
  var y int = -4500
  fmt.Printf("Type: %T, value: %v", x, x)
  fmt.Printf("Type: %T, value: %v", y, y)
}

Go 有五个关键字 / 有符号整数类型:

TypeSizeRange
intDepends on platform: 32 bits in 32 bit systems and 64 bit in 64 bit systems-2147483648 to 2147483647 in 32 bit systems and -9223372036854775808 to 9223372036854775807 in 64 bit systems
int88 bits/1 byte-128 to 127
int1616 bits/2 byte-32768 to 32767
int3232 bits/4 byte-2147483648 to 2147483647
int6464 bits/8 byte-9223372036854775808 to 9223372036854775807

无符号整数

uint 关键字声明有符号整数,只可以存储非负值:

package main
import ("fmt")

func main() {
  var x uint = 500
  var y uint = 4500
  fmt.Printf("Type: %T, value: %v", x, x)
  fmt.Printf("Type: %T, value: %v", y, y)
}

Go- 有五个关键字 / 无符号整数类型:

TypeSizeRange
uintDepends on platform: 32 bits in 32 bit systems and 64 bit in 64 bit systems0 to 4294967295 in 32 bit systems and 0 to 18446744073709551615 in 64 bit systems
uint88 bits/1 byte0 to 255
uint1616 bits/2 byte0 to 65535
uint3232 bits/4 byte0 to 4294967295
uint6464 bits/8 byte0 to 18446744073709551615

使用哪种整数类型?

要选择的整数类型取决于变量必须存储的值。

此示例将导致一个错误,因为 1000 超出了 int8 的范围(从-128到127):

package main
import ("fmt")

func main() {
  var x int8 = 1000
  fmt.Printf("Type: %T, value: %v", x, x)
}
// Result:
// ./prog.go:5:7: constant 1000 overflows int8

浮点数

浮点数据类型用于存储带小数点的正数和负数,如35.3、-2.34或3597.34987。

浮点数据类型有两个关键字,对应两种数据精度:

TypeSizeRange
float3232 bits-3.4e+38 to 3.4e+38.
float6464 bits-1.7e+308 to +1.7e+308.

float32

此示例说明如何声明float32类型的一些变量:

package main
import ("fmt")

func main() {
  var x float32 = 123.78
  var y float32 = 3.4e+38
  fmt.Printf("Type: %T, value: %v\n", x, x)
  fmt.Printf("Type: %T, value: %v", y, y)
}

float64

float64数据类型可以存储比float32更大的一组数字。

此示例说明如何声明float64类型的变量:

package main
import ("fmt")

func main() {
  var x float64 = 1.7e+308
  fmt.Printf("Type: %T, value: %v", x, x)
}

字符串

字符串数据类型用于存储一系列字符(文本)。字符串值必须用双引号括起来:

package main
import ("fmt")

func main() {
  var txt1 string = "Hello!"
  var txt2 string
  txt3 := "World 1"

  fmt.Printf("Type: %T, value: %v\n", txt1, txt1)
  fmt.Printf("Type: %T, value: %v\n", txt2, txt2)
  fmt.Printf("Type: %T, value: %v\n", txt3, txt3)
}
/*
Result:
Type: string, value: Hello!
Type: string, value:
Type: string, value: World 1
*/

Go 数组

数组用于将同一类型的多个值存储在一个变量中,而不是为每个值声明单独的变量。

数组声明

在 Go 语言中,有两种方式声明数组:

1. 使用 var 关键字:

语法
var array_name = [length]datatype{values} // here length is defined

或者

var array_name = [...]datatype{values} // here length is inferred
Note: 不指定长度时, [...] 是必须的

2. 使用 := 符号:

语法
array_name := [length]datatype{values} // here length is defined

或者

array_name := [...]datatype{values} // here length is inferred
Note: 不指定长度时, [...] 是必须的

length 指定可存储在数组中的元素数量。Go 语言中数组的长度是固定的。数组的长度要么是由定义时指定,要么是由初始赋值时编译器推断(未指定长度时,编译器根据赋值数量推断数组长度).

数组示例

此示例声明了两个指定了长度的数组(arr1和arr2):

package main
import ("fmt")

func main() {
  var arr1 = [3]int{1,2,3}
  arr2 := [5]int{4,5,6,7,8}

  fmt.Println(arr1)
  fmt.Println(arr2)
}
/*
Result:
[1 2 3]
[4 5 6 7 8]
*/

此示例声明了两个具有推断长度的数组(arr1和arr2):

package main
import ("fmt")

func main() {
  var arr1 = [...]int{1,2,3}
  arr2 := [...]int{4,5,6,7,8}

  fmt.Println(arr1)
  fmt.Println(arr2)
}
/*
Result:
[1 2 3]
[4 5 6 7 8]
*/

此示例声明一个字符串数组:

package main
import ("fmt")

func main() {
  var cars = [4]string{"Volvo", "BMW", "Ford", "Mazda"}
  fmt.Print(cars)
}
/*
Result:
[Volvo BMW Ford Mazda]
*/

访问数组的元素

可以通过索引号来访问特定的数组元素。在 Go 中,数组索引从 0 开始。即 [0] 是第一个元素,[1] 是第二个元素,依次类推。

此示例说明如何访问 prices 数组中的第一个和第三个元素:

package main
import ("fmt")

func main() {
  prices := [3]int{10,20,30}

  fmt.Println(prices[0])
  fmt.Println(prices[2])
}
/*
Result:
10
30
*/

修改数组的元素

和数组访问一样,可以通过索引号来更改特定数组元素的值。

此示例说明如何更改 prices 数组中第三个元素的值:

package main
import ("fmt")

func main() {
  prices := [3]int{10,20,30}

  prices[2] = 50
  fmt.Println(prices)
}
/*
Result:
[10 20 50]
*/

数组初始化

默认值

如果一个数组或它其中的某个元素在代码中尚未初始化,则会为它分配其对应类型的默认值。

如下示例:

package main
import ("fmt")

func main() {
  arr1 := [5]int{} //not initialized
  arr2 := [5]int{1,2} //partially initialized
  arr3 := [5]int{1,2,3,4,5} //fully initialized

  fmt.Println(arr1)
  fmt.Println(arr2)
  fmt.Println(arr3)
}
/*
Result:
[0 0 0 0 0]
[1 2 0 0 0]
[1 2 3 4 5]
*/

仅初始化特定元素

可以只初始化数组中的特定元素。

此示例仅初始化数组的第二个和第三个元素:

package main
import ("fmt")

func main() {
  arr1 := [5]int{1:10,2:40}

  fmt.Println(arr1)
}
/*
Result:
[0 10 40 0 0]
*/

示例说明

上面的数组有5个元素。

  • 1:10 表示:将10分配给数组索引1(第二个元素)。
  • 2:40 表示:将40分配给数组索引2(第三个元素)。

数组长度获取

len() 函数用于获取数组的长度:

package main
import ("fmt")

func main() {
  arr1 := [4]string{"Volvo", "BMW", "Ford", "Mazda"}
  arr2 := [...]int{1,2,3,4,5,6}

  fmt.Println(len(arr1))
  fmt.Println(len(arr2))
}
/*
Result:
4
6
*/

Go 切片

创建切片

切片与数组相似,但更强大、更灵活。

与数组一样,切片也用于在单个变量中存储相同类型的多个值。

然而,与数组不同的是,切片的长度可以根据需要增长或收缩。

在Go中,有几种方法可以创建切片:

  • 使用 []datatype{values} 格式
  • 从数组创建切片
  • 使用 make() 函数

使用 []datatype{values} 格式创建切片

语法
slice_name := []datatype{values}

声明切片的常见方法如下:

myslice := []int{}

上面的代码声明了一个长度为0、容量为0的空切片。

要在声明时初始化切片,可以使用以下方法:

myslice := []int{1,2,3}

上面的代码声明了一个长度为3、容量为3的整数切片。

Go 语言中,有两种函数分别用来获取切片的长度和容量:

  • len() 函数:返回切片的长度(切片中的元素数量)
  • cap() 函数:返回切片的容量(切片可以增大到或缩小到的元素数)
示例

如下示例展示了如何使用[]datatype{values}格式创建切片并使用上述函数获取其长度和容量:

package main
import ("fmt")

func main() {
  myslice1 := []int{}
  fmt.Println(len(myslice1))
  fmt.Println(cap(myslice1))
  fmt.Println(myslice1)

  myslice2 := []string{"Go", "Slices", "Are", "Powerful"}
  fmt.Println(len(myslice2))
  fmt.Println(cap(myslice2))
  fmt.Println(myslice2)
}
/*
0
0
[]
4
4
[Go Slices Are Powerful]
*/

在上面的例子中,可以看到在第一个切片(myslice1)中,没有指定实际元素,因此切片的长度和容量都将为零。在第二个切片(myslice2)中,指定了元素,并且长度和容量都等于指定的实际元素的数量。

从数组创建切片

也可以通过对数组进行切片来创建切片:

语法
var myarray = [length]datatype{values} // An array
myslice := myarray[start:end] // A slice made from the array
示例

使用示例说明如何从数组创建切片:

package main
import ("fmt")

func main() {
  arr1 := [6]int{10, 11, 12, 13, 14,15}
  myslice := arr1[2:4]

  fmt.Printf("myslice = %v\n", myslice)
  fmt.Printf("length = %d\n", len(myslice))
  fmt.Printf("capacity = %d\n", cap(myslice))
}
/*
myslice = [12 13]
length = 2
capacity = 4
*/

在上面的例子中,myslice 是一个长度为 2 的切片。它由arr1组成,arr1 是一个长度为6的数组。

切片数组的第三个元素开始(值为 12 的元素,数组索引从 0 开始), 切片可以增长到数组的末尾。这意味着切片的容量为 4。

如果 myslice 从索引 0 开始,则切片容量将为 6。

使用 make() 函数创建切片

可以使用 make() 函数创建切片。

语法
slice_name := make([]type, length, capacity)
Note: 如果不指定 capacity 参数,它将等于 length。
示例

以下示例说明了如何使用 make() 函数创建切片:

package main
import ("fmt")

func main() {
  myslice1 := make([]int, 5, 10)
  fmt.Printf("myslice1 = %v\n", myslice1)
  fmt.Printf("length = %d\n", len(myslice1))
  fmt.Printf("capacity = %d\n", cap(myslice1))

  // with omitted capacity
  myslice2 := make([]int, 5)
  fmt.Printf("myslice2 = %v\n", myslice2)
  fmt.Printf("length = %d\n", len(myslice2))
  fmt.Printf("capacity = %d\n", cap(myslice2))
}
/*
myslice1 = [0 0 0 0 0]
length = 5
capacity = 10
myslice2 = [0 0 0 0 0]
length = 5
capacity = 5
*/

访问切片元素

和数组一样,通过索引号访问特定的切片元素,索引从 0 开始,[0] 表明访问第一个元素,[1] 表明访问第二个元素,以此类推。

访问切片的第一个和第三个元素的示例如下:

package main
import ("fmt")

func main() {
  prices := []int{10,20,30}

  fmt.Println(prices[0])
  fmt.Println(prices[2])
}
/*
10
30
*/

修改切片元素

通过引用索引号来修改特定的切片元素。

如下示例说明了如何修改切片中的第三个元素:

package main
import ("fmt")

func main() {
  prices := []int{10,20,30}
  prices[2] = 50
  fmt.Println(prices[0])
  fmt.Println(prices[2])
}
/*
10
50
*/

添加切片元素

使用 append() 函数将元素添加到切片的末尾:

语法

slice_name = append(slice_name, element1, element2, ...)

示例

如下示例说明了如何将元素添加到切片的末尾:

package main
import ("fmt")

func main() {
  myslice1 := []int{1, 2, 3, 4, 5, 6}
  fmt.Printf("myslice1 = %v\n", myslice1)
  fmt.Printf("length = %d\n", len(myslice1))
  fmt.Printf("capacity = %d\n", cap(myslice1))

  myslice1 = append(myslice1, 20, 21)
  fmt.Printf("myslice1 = %v\n", myslice1)
  fmt.Printf("length = %d\n", len(myslice1))
  // 为什么容量变成了 12 而不是 8 ?
  fmt.Printf("capacity = %d\n", cap(myslice1))
}
/*
myslice1 = [1 2 3 4 5 6]
length = 6
capacity = 6
myslice1 = [1 2 3 4 5 6 20 21]
length = 8
capacity = 12
*/

添加一个切片到另一个切片

要将一个切片的所有元素附加到另一个切片,使用 append() 函数:

语法

slice3 = append(slice1, slice2...)
当 slice2 是一个切片时,即将一个切片添加到另一个切片时,slice2 后的 ... 是必须有的。

示例

package main
import ("fmt")

func main() {
  myslice1 := []int{1,2,3}
  myslice2 := []int{4,5,6}
  myslice3 := append(myslice1, myslice2...)

  fmt.Printf("myslice3=%v\n", myslice3)
  fmt.Printf("length=%d\n", len(myslice3))
  fmt.Printf("capacity=%d\n", cap(myslice3))
}
/*
myslice3=[1 2 3 4 5 6]
length=6
capacity=6
*/

修改切片长度

数组是固定长度的,但切片不是,切片可以修改其长度。以下示例说明了如何修改一个切片的长度:

package main
import ("fmt")

func main() {
  arr1 := [6]int{9, 10, 11, 12, 13, 14} // An array
  myslice1 := arr1[1:5] // Slice array
  fmt.Printf("myslice1 = %v\n", myslice1)
  fmt.Printf("length = %d\n", len(myslice1))
  fmt.Printf("capacity = %d\n", cap(myslice1))

  myslice1 = arr1[1:3] // Change length by re-slicing the array
  fmt.Printf("myslice1 = %v\n", myslice1)
  fmt.Printf("length = %d\n", len(myslice1))
  fmt.Printf("capacity = %d\n", cap(myslice1))

  myslice1 = append(myslice1, 20, 21, 22, 23) // Change length by appending items
  fmt.Printf("myslice1 = %v\n", myslice1)
  fmt.Printf("length = %d\n", len(myslice1))
  fmt.Printf("capacity = %d\n", cap(myslice1))
}
/*
myslice1 = [10 11 12 13]
length = 4
capacity = 5
myslice1 = [10 11]
length = 2
capacity = 5
myslice1 = [10 11 20 21 22 23]
length = 6
capacity = 10
*/

内存效率

使用切片时,Go会将所有底层元素加载到内存中。

如果切片很大,并且只需要几个元素,那么最好使用 copy() 函数来复制这些元素。

copy() 函数创建一个新的底层数组,该数组只包含切片所需的元素。这将减少程序的内存消耗。

语法

copy(dest, src)
copy() 函数接收 destsrc 两个切片,并将数据从 src 复制到 dest 。它返回复制的元素数。

示例

package main
import ("fmt")

func main() {
  numbers := []int{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}
  // Original slice
  fmt.Printf("numbers = %v\n", numbers)
  fmt.Printf("length = %d\n", len(numbers))
  fmt.Printf("capacity = %d\n", cap(numbers))

  // Create copy with only needed numbers
  neededNumbers := numbers[:len(numbers)-10]
  numbersCopy := make([]int, len(neededNumbers))
  copy(numbersCopy, neededNumbers)

  fmt.Printf("numbersCopy = %v\n", numbersCopy)
  fmt.Printf("length = %d\n", len(numbersCopy))
  fmt.Printf("capacity = %d\n", cap(numbersCopy))
}
/*
// Original slice
numbers = [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
length = 15
capacity = 15
// New slice
numbersCopy = [1 2 3 4 5]
length = 5
capacity = 5
*/

运算符用于对变量和值执行运算。

+ 运算符将两个值相加在一起,如下示例所示:

package main
import ("fmt")

func main() {
  var a = 15 + 25
  fmt.Println(a)
}

+ 运算符也可以用于将变量和值相加,或将变量和另一个变量相加:

package main
import ("fmt")

func main() {
  var (
    sum1 = 100 + 50 // 150 (100 + 50)
    sum2 = sum1 + 250 // 400 (150 + 250)
    sum3 = sum2 + sum2 // 800 (400 + 400)
  )
  fmt.Println(sum3)
}

Go 语言中将运算符分为以下组:

  • 算术运算符
  • 赋值运算符
  • 比较运算符
  • 逻辑运算符
  • 位运算符

Go 运算符

算术运算符

算术运算符用于执行常见的数学运算。

运算符名称描述示例
+将两个值相加x + y
-从另一个值中减去一个值x - y
*将两个值相乘x * y
/将一个值除以另一个值x / y
%求余返回除法余数x % y
++自增将变量的值增加1x++
--自减将变量的值减少1x--

示例

package main
import ("fmt")
func main() {

  x:= 10
  y:= 2
  z:= 8
  s:= 3

  fmt.Println(x+y)
  fmt.Println(x-y)
  fmt.Println(x*y)
  fmt.Println(x/y)
  fmt.Println(z%s)
  x++
  fmt.Println(x)
  y--
  fmt.Println(y)
}
/*
12
8
20
5
2
11
1
*/

赋值运算符

赋值运算符用于为变量赋值。

所有赋值运算符如下:

运算符示例等同于
=x = 5x = 5
+=x += 3x = x + 3
-=x -= 3x = x - 3
*=x *= 3x = x * 3
/=x /= 3x = x / 3
%=x %= 3x = x % 3
&=x &= 3x = x & 3
\=x\= 3x = x\3
^=x ^= 3x = x ^ 3

示例

package main
import ("fmt")
func main() {

  var x = 5
  fmt.Println(x)
  x +=3
  fmt.Println(x)
  x -=3
  fmt.Println(x)
  x *= 3
  fmt.Println(x)
  x /= 3
  fmt.Println(x)
  x %= 3
  fmt.Println(x)
  // 重新设置x为5
  x = 5
  fmt.Printf("x is %b \n", x)  // 101
  fmt.Printf("3 is %03b \n", 3) // 011
  x &= 3
  fmt.Printf("x now is %03b \n", x) // 001

  var x = 5
  fmt.Printf("x is %b \n", x)  // 101
  fmt.Printf("3 is %03b \n", 3) // 011
  x |= 3
  fmt.Printf("x now is %03b \n", x) // 111
  var x = 5

  fmt.Printf("x is %b \n", x)  // 101
  fmt.Printf("3 is %03b \n", 3) // 011

  x ^= 3

  fmt.Printf("x now is %03b \n", x) // 110

  var x = 5

  fmt.Printf("x is %b \n", x)  // 101

  x >>= 3

  fmt.Printf("x now is %03b \n", x) // 000
  var x = 5

  fmt.Printf("x is %b \n", x)  // 101

  x <<= 3

  fmt.Printf("x now is %03b \n", x) // 101000
}
/*
5
8
5
15
5
2
x is 101
3 is 011
x now is 001
x is 101
3 is 011
x now is 111
x is 101
3 is 011
x now is 110
x is 101
x now is 000
x is 101
x now is 101000
*/

比较运算符

比较运算符用于比较两个值。

Note:比较的返回值为 true(1)或 false(0)。

在如下示例中,使用大于运算符(>)来确定 5 是否大于 3 :

package main
import ("fmt")

func main() {
  var x = 5
  var y = 3
  fmt.Println(x>y) // returns 1 (true) because 5 is greater than 3
}

所有比较运算符如下表:

运算符名称示例
==等于x == y
!=不等于x != y
>大于x > y
<小于x < y
>=大于或等于x >= y
<=小于或等于x <= y

示例

package main
import ("fmt")

func main() {
  var x = 5
  var y = 3
  var z = 5
  fmt.Println(x>y)
  fmt.Println(x<y)
  fmt.Println(x>=z)
  fmt.Println(x<=y)
  fmt.Println(x!=y)
  fmt.Println(x==y)
}
/*
true
false
true
false
true
false
*/

逻辑运算符

逻辑运算符用于确定变量或值之间的逻辑:

运算符名称描述示例
&&逻辑与如果两个语句都为true,则返回truex < 5 && x < 10
\\ 逻辑或如果其中一个语句为true,则返回truex < 5\\x < 4
!逻辑非反转结果,如果结果为true则返回false!(x < 5 && x < 10)

示例

package main
import ("fmt")
func main() {

  var x = 5

  fmt.Println(x < 5 &&  x < 10) 
  fmt.Println(x < 5 || x < 4) 
  fmt.Println(!(x < 5 && x < 10)) 
}
/*
false
false
true
*/

位运算符

按位运算符用于(二进制)数字:

运算符名称描述示例
&ANDSets each bit to 1 if both bits are 1x & y
\ ORSets each bit to 1 if one of two bits is 1x\y
^XORSets each bit to 1 if only one of two bits is 1x ^ b
<<Zero fill left shiftShift left by pushing zeros in from the rightx << 2
>>Signed right shiftShift right by pushing copies of the leftmost bit in from the left, and let the rightmost bits fall offx >> 2

示例

package main
import ("fmt")
func main() {

  var x = 9
  var y = 8

  fmt.Printf("x = %b\n",x)
  fmt.Printf("y = %b\n",y)
  fmt.Printf("x & y is %b\n",x & y)
  fmt.Printf("x | y is %b\n",x | y)
  fmt.Printf("x ^ y is %b\n",x ^ y)
  fmt.Printf("x << 2 is %b\n",x << 2)
  fmt.Printf("x >> 2 is %b\n",x >> 2)
}
/*
x = 1001
y = 1000
x & y is 1000
x | y is 1001
x ^ y is 1
x << 2 is 100100
x >> 2 is 10
*/

Go 条件语句

条件语句用于根据不同的条件选择执行不同的操作。

条件(Conditions)

条件的值是 true 或者 false,或者对应值的表达式。

Go 支持数学中常见的比较运算符:

  • 小于 <
  • 小于或等于 <=
  • 大于 >
  • 大于或等于 >=
  • 等于 ==
  • 不等于 !=

此外,Go 支持常见的逻辑运算符:

  • 逻辑与 &&
  • 逻辑或 ||
  • 逻辑非 !

可以使用这些运算符或它们的组合为不同的情况编写条件:

示例
x > y
x != y
(x > y) && (y > z)
(x == y)\\z

Go 具有以下条件语句:

  • 使用 if 指定条件为 true 时要执行的代码块
  • 使用 else 指定上述条件为 false 时要执行的代码块
  • 使用 else if 指定新的条件判断,用于上述 if 条件为 false 时执行,并在新条件判断为 true 时运行指定的代码块
  • 使用 switch 指定多个满足条件的可执行代码块

if 语句

如果条件为 true,则执行 if 语句指定 Go 代码块。

语法

if condition {
  // code to be executed if condition is true
}

示例

如下示例中,判断条件 20 > 18 是否为 true, 如果该条件为 true, 打印文本:

package main
import ("fmt")

func main() {
  if 20 > 18 {
    fmt.Println("20 is greater than 18")
  }
  // 判断变量大小
  x:= 20
  y:= 18
  if x > y {
    fmt.Println("x is greater than y")
  }
}
/*
20 is greater than 18
x is greater than y
*/

if else 语句

如果条件为 true,则执行 if 语句指定 Go 代码块,否则执行 else 语句指定的 Go 代码块。

语法

if condition {
  // code to be executed if condition is true
} else {
  // code to be executed if condition is false
}

示例

如下示例中,如果变量 time 的值小于 18 ,将会执行 if 指定的代码,打印出 "Good day",否则将会执行 else 指定的代码块,打印出 "Good evening."

package main
import ("fmt")

func main() {
  time := 20
  if (time < 18) {
    fmt.Println("Good day.")
  } else {
    fmt.Println("Good evening.")
  }
}

注意,在 else 语法中,花括号必须是 } else { 格式,否则会报错

package main
import ("fmt")

func main() {
  temperature := 14
  if (temperature > 15) {
    fmt.Println("It is warm out there.")
  } // this raises an error
  else {
    fmt.Println("It is cold out there.")
  }
}
/*
./prog.go:9:3: syntax error: unexpected else, expecting }
*/

else if 语句

如果第一个条件为 false,可以使用 else if 语句指定一个新条件判断。

语法

if condition1 {
   // code to be executed if condition1 is true
} else if condition2 {
   // code to be executed if condition1 is false and condition2 is true
} else {
   // code to be executed if condition1 and condition2 are both false
}

示例

package main
import ("fmt")

func main() {
  time := 22
  if time < 10 {
    fmt.Println("Good morning.")
  } else if time < 20 {
    fmt.Println("Good day.")
  } else {
    fmt.Println("Good evening.")
  }
}
/*
Good evening.
*/

在上面的例子中,time (22) 大于 10,因此第一个条件为 false。else if 语句中的下一个条件也是 false ,因此我们转到else条件,因为条件1和条件2都是false,并打印到屏幕上"Good evening"。如果 time 是14,程序将打印 "Good day."

嵌套 if 语句

在 if 语句中包含 if 语句,称为嵌套 if。

语法

if condition1 {
   // code to be executed if condition1 is true
  if condition2 {
     // code to be executed if both condition1 and condition2 are true
  }
}

示例

package main
import ("fmt")

func main() {
  num := 20
  if num >= 10 {
    fmt.Println("Num is more than 10.")
    if num > 15 {
      fmt.Println("Num is also more than 15.")
     }
  } else {
    fmt.Println("Num is less than 10.")
  }
}
/*
Num is more than 10.
Num is also more than 15.
*/

Go switch 语句

使用 switch 语句可以从多个要执行的代码块中选择一个。

Go 中的 switch 语句与 C、C++、Java、JavaScript 和 PHP 中的类似。不同之处在于,它只运行匹配的 case,因此不需要 break 语句。

单值 switch 语法

语法

switch expression {
case x:
   // code block
case y:
   // code block
case z:
...
default:
   // code block
}

工作原理说明:

  • expression 求值一次
  • switch expression 的值和每个 case 的值进比较
  • 如果匹配,则执行相关的代码块
  • default 是可选的. 用于指定没有任何 case 匹配时执行的代码

示例

package main
import ("fmt")

func main() {
  day := 4

  switch day {
  case 1:
    fmt.Println("Monday")
  case 2:
    fmt.Println("Tuesday")
  case 3:
    fmt.Println("Wednesday")
  case 4:
    fmt.Println("Thursday")
  case 5:
    fmt.Println("Friday")
  case 6:
    fmt.Println("Saturday")
  case 7:
    fmt.Println("Sunday")
  }
}
/*
Thursday
*/

default 关键字

default 关键字用于指定在没有 case 匹配的情况下要运行的代码:

package main
import ("fmt")

func main() {
  day := 8

  switch day {
  case 1:
    fmt.Println("Monday")
  case 2:
    fmt.Println("Tuesday")
  case 3:
    fmt.Println("Wednesday")
  case 4:
    fmt.Println("Thursday")
  case 5:
    fmt.Println("Friday")
  case 6:
    fmt.Println("Saturday")
  case 7:
    fmt.Println("Sunday")
  default:
    fmt.Println("Not a weekday")
  }
}
/*
Not a weekday
*/

注意类型

所有 case 值都应具有与 switch 表达式相同的类型。否则,编译器将会报错:

package main
import ("fmt")

func main() {
  a := 3

  switch a {
  case 1:
    fmt.Println("a is one")
  case "b":
    fmt.Println("a is b")
  }
}
/*
./prog.go:11:2: cannot use "b" (type untyped string) as type int
*/

多值 switch 语法

语法

switch 语句中的每个 case 都可以有多个值:

switch expression {
case x,y:
   // code block if expression is evaluated to x or y
case v,w:
   // code block if expression is evaluated to v or w
case z:
...
default:
   // code block if expression is not found in any cases
}

示例

package main
import ("fmt")

func main() {
   day := 5

   switch day {
   case 1,3,5:
    fmt.Println("Odd weekday")
   case 2,4:
     fmt.Println("Even weekday")
   case 6,7:
    fmt.Println("Weekend")
  default:
    fmt.Println("Invalid day of day number")
  }
}
/*
Odd weekday
*/

Go 循环语句

for 循环

for 循环会循环执行指定次数的内部代码块。 for 循环是 Go 语言中的唯一可用循环。

语法

如果想一次又一次地运行相同的代码,每次都使用不同的值,那么循环是最佳选择。

循环的每次执行都被称为迭代。

for 循环最多可以使用三个语句 (statement):

for statement1; statement2; statement3 {
   // code to be executed for each iteration
}

示例 1

打印 0 到 4 之间的数字:

package main
import ("fmt")

func main() {
  for i:=0; i < 5; i++ {
    fmt.Println(i)
  }
}
/*
0
1
2
3
4
*/

示例解释:

  • i:=0; - 初始化循环计数器(i),并将起始值设置为0
  • i < 5; - 只要 i 小于 5,就继续循环
  • i++ - 每次迭代将循环计数器值增加 1

示例 2

在0-100,每隔10打印一次(输出100以内10的倍数):

package main
import ("fmt")

func main() {
  for i:=0; i <= 100; i+=10 {
    fmt.Println(i)
  }
}
/*
0
10
20
30
40
50
60
70
80
90
100
*/

示例 2 说明:

  • i:=0; - 初始化循环计数器(i),并将起始值设置为0
  • i <= 100; - 只要 i 小于或等于 100 ,就继续循环
  • i+=10 - 每次迭代将循环计数器值增加 10

continue 语句

continue 语句用于跳过循环中的一个或多个迭代。然后继续循环中的下一次迭代。

示例

跳过 3 :

package main
import ("fmt")

func main() {
  for i:=0; i < 5; i++ {
    if i == 3 {
      continue
    }
   fmt.Println(i)
  }
}
/*
0
1
2
4
*/

break 语句

break 语句用于中断/终止循环执行。

示例

当 i = 3 时中断循环,执行循环后的语句:

package main
import ("fmt")

func main() {
  for i:=0; i < 5; i++ {
    if i == 3 {
      break
    }
   fmt.Println(i)
  }
}
/*
0
1
2
*/
Note: continue 和 break 通常与条件一起使用。

嵌套循环

允许将一个循环放入另一个循环中,此时,内部循环会在外部循环的每一个迭代都执行一次:

package main
import ("fmt")

func main() {
  adj := [2]string{"big", "tasty"}
  fruits := [3]string{"apple", "orange", "banana"}
  for i:=0; i < len(adj); i++ {
    for j:=0; j < len(fruits); j++ {
      fmt.Println(adj[i],fruits[j])
    }
  }
}
/*
big apple
big orange
big banana
tasty apple
tasty orange
tasty banana
*/

range 关键字

使用range 关键字可以更方便的对数组、切片或映射进行迭代。它同时返回索引和值。

语法

for index, value := array|slice|map {
   // code to be executed for each iteration
}

示例

此示例使用 range 迭代数组,并打印每个数组的索引和值(idx存储索引,val存储值):

package main
import ("fmt")

func main() {
  fruits := [3]string{"apple", "orange", "banana"}
  for idx, val := range fruits {
     fmt.Printf("%v\t%v\n", idx, val)
  }
}
/*
0      apple
1      orange
2      banana
*/

如果只想使用值,不需要索引,那么可以将索引返回的变量使用 _ 表示省略(反过来只使用索引不使用值类似):

package main
import ("fmt")

func main() {
  fruits := [3]string{"apple", "orange", "banana"}
  for _, val := range fruits {
     fmt.Printf("%v\n", val)
  }
}
/*
apple
orange
banana
*/

Go 函数

函数是一组可以在程序中重复使用的语句。

加载页时,函数不会自动执行。

函数将通过对该函数的调用来执行。

创建函数

语法

要创建(通常称为声明)函数,请执行以下操作:

  • 使用 func 关键字.
  • 指定函数名, 后面跟着括号 ().
  • 最后,在花括号{}内定义函数应该做什么的代码。
func FunctionName() {
  // code to be executed
}

函数调用

函数不会立即执行。它们被“保存以供以后使用”,并将在调用时执行。

以下示例中,首先创建了一个名为 myMessage() 的函数。左花括号({)表示函数代码的开始,右花括号(})表示函数的结束。函数输出 "I just got executed!" 。要调用该函数,只需将其名称后跟两个圆括号():

package main
import ("fmt")

func myMessage() {
  fmt.Println("I just got executed!")
}

func main() {
  myMessage() // call the function
}
/*
I just got executed!
*/

函数允许调用多次:

package main
import ("fmt")

func myMessage() {
  fmt.Println("I just got executed!")
}

func main() {
  myMessage() // call the function
  myMessage()
  myMessage()
}
/*
I just got executed!
I just got executed!
I just got executed!
*/

Go函数的命名规则

  • 函数名必须以字母开头
  • 函数名只能包含字母、数字字符和下划线(A-z0-9_
  • 函数名区分大小写
  • 函数名不能有空格
  • 如果函数名由多个单词组成,可以沿用变量的多词命名规则

Tip: 函数名最好能够反映函数功能。

函数形参/实参

信息可以作为参数传递给函数。参数(形参)充当函数内部的变量。

参数及其类型在函数名后面的括号内指定。您可以添加任意数量的参数,只需用逗号将它们隔开:

func FunctionName(param1 type, param2 type, param3 type) {
  // code to be executed
}

如下示例有一个函数,它有一个字符串类型的参数(fname)。当调用familyName()函数时,我们还传递一个名称(例如Liam),该名称用于函数内部,该函数输出几个不同的名字,但姓氏相同:

package main
import ("fmt")

func familyName(fname string) {
  fmt.Println("Hello", fname, "Refsnes")
}

func main() {
  familyName("Liam")
  familyName("Jenny")
  familyName("Anja")
}
/*
Hello Liam Refsnes
Hello Jenny Refsnes
Hello Anja Refsnes
*/

在函数内部,您可以添加任意数量的参数:

package main
import ("fmt")

// fname, age 为形式参数
func familyName(fname string, age int) {
  fmt.Println("Hello", age, "year old", fname, "Refsnes")
}

func main() {
  // "Liam", 3 为实际参数, 即实参,实参会传递给对应的形参
  familyName("Liam", 3)
  familyName("Jenny", 14)
  familyName("Anja", 30)
}
/*
Hello 3 year old Liam Refsnes
Hello 14 year old Jenny Refsnes
Hello 30 year old Anja Refsnes
*/

注意调用函数时,传入的参数数量和顺序需要和定义一致。

函数返回

语法

如果希望函数返回值,则需要定义返回值的数据类型(如int、string等),并在函数内部使用 return 关键字:

func FunctionName(param1 type, param2 type) type {
  // code to be executed
  return output
}

如下示例中,myFunction() 接收两个整型参数(xy)并以整型格式(int)返回这两个数的和(x + y):

package main
import ("fmt")

func myFunction(x int, y int) int {
  return x + y
}

func main() {
  fmt.Println(myFunction(1, 2))
}
/*
3
*/

命名返回值

在 Go 语言中,可以在函数声明是命名返回值,函数内对该返回值变量赋值返回即可:

package main
import ("fmt")

func myFunction(x int, y int) (result int) {
  result = x + y
  return
  // 也可以指定变量名返回,效果一样
  // return result
}

func main() {
  fmt.Println(myFunction(1, 2))
}
/*
3
*/

使用变量存储返回值

可以将返回值存储在变量中,如下所示,将函数返回值存储在 total 变量中:

package main
import ("fmt")

func myFunction(x int, y int) (result int) {
  result = x + y
  return
}

func main() {
  total := myFunction(1, 2)
  fmt.Println(total)
}
/*
3
*/

多个返回值

函数可以返回多个值。如下,函数 myFunction() 返回一个整型 (result) 和一个字符串 (txt1):

package main
import ("fmt")

func myFunction(x int, y string) (result int, txt1 string) {
  result = x + x
  txt1 = y + " World!"
  return
}

func main() {
  fmt.Println(myFunction(5, "Hello"))
  // 用变量存储返回值如下:
  // a, b := myFunction(5, "Hello")
  // fmt.Println(a, b)
}
/*
10 Hello World!
*/

递归函数

Go 支持递归函数。如果函数调用自身并达到停止条件,则它是递归的。

如下示例中, testcount() 是一个调用自身的函数。 使用 x 变量作为数据, 在每次递归时增加 1 (x + 1) , 递归的结束条件是单 x等于 11 (x == 11)。

package main
import ("fmt")

func testcount(x int) int {
  if x == 11 {
    return 0
  }
  fmt.Println(x)
  return testcount(x + 1)
}

func main(){
  testcount(1)
}
/*
1
2
3
4
5
6
7
8
9
10
*/

递归是一个常见的数学和编程概念。这样做的好处是,您可以重复执行相同步骤获得结果。

开发人员应该小心递归函数,因为编写一个永远不会终止的函数,或者一个使用过多内存或处理器能力的函数很容易出错。然而,如果写得正确,递归可能是一种非常高效且数学上优雅的编程方法。

在以下示例中,factorial_recursion() 是一个调用自身的函数。我们使用 x 变量作为数据,每次递归时它都会递减( -1 )。当条件不大于 0 时(即当条件为 0 时),递归结束。

package main
import ("fmt")

func factorial_recursion(x float64) (y float64) {
  if x > 0 {
     y = x * factorial_recursion(x-1)
  } else {
     y = 1
  }
  return
}

func main() {
  fmt.Println(factorial_recursion(4))
}

Go 结构体

struct(structure 的缩写)用于将不同数据类型的成员集合创建为单个变量。

数组用于将同一数据类型的多个值存储到单个变量中,而结构体用于将不同数据类型的多个值存储到一个变量中。

结构体声明

在 Go 语言中,声明一个结构体需要使用 typestruct 关键字

type struct_name struct {
  member1 datatype;
  member2 datatype;
  member3 datatype;
  ...
}

如下示例中,声明了一个 Person 结构体,包含几个成员:name, age, jobsalary:

type Person struct {
  name string
  age int
  job string
  salary int
}

访问结构体成员

要访问结构的任何成员,在结构变量名和结构成员之间使用点运算符(.):

package main
import ("fmt")

type Person struct {
  name string
  age int
  job string
  salary int
}

func main() {
  var pers1 Person
  var pers2 Person

  // Pers1 specification
  pers1.name = "Hege"
  pers1.age = 45
  pers1.job = "Teacher"
  pers1.salary = 6000

  // Pers2 specification
  pers2.name = "Cecilie"
  pers2.age = 24
  pers2.job = "Marketing"
  pers2.salary = 4500

  // Access and print Pers1 info
  fmt.Println("Name: ", pers1.name)
  fmt.Println("Age: ", pers1.age)
  fmt.Println("Job: ", pers1.job)
  fmt.Println("Salary: ", pers1.salary)

  // Access and print Pers2 info
  fmt.Println("Name: ", pers2.name)
  fmt.Println("Age: ", pers2.age)
  fmt.Println("Job: ", pers2.job)
  fmt.Println("Salary: ", pers2.salary)
}
/*
Name: Hege
Age: 45
Job: Teacher
Salary: 6000
Name: Cecilie
Age: 24
Job: Marketing
Salary: 4500
*/

将结构体作为函数参数传递

可以将结构体作为函数参数传递,如下所示:

package main
import ("fmt")

type Person struct {
  name string
  age int
  job string
  salary int
}

func main() {
  var pers1 Person
  var pers2 Person

  // Pers1 specification
  pers1.name = "Hege"
  pers1.age = 45
  pers1.job = "Teacher"
  pers1.salary = 6000

  // Pers2 specification
  pers2.name = "Cecilie"
  pers2.age = 24
  pers2.job = "Marketing"
  pers2.salary = 4500

  // Print Pers1 info by calling a function
  printPerson(pers1)

  // Print Pers2 info by calling a function
  printPerson(pers2)
}

func printPerson(pers Person) {
  fmt.Println("Name: ", pers.name)
  fmt.Println("Age: ", pers.age)
  fmt.Println("Job: ", pers.job)
  fmt.Println("Salary: ", pers.salary)
}
/*
Name: Hege
Age: 45
Job: Teacher
Salary: 6000
Name: Cecilie
Age: 24
Job: Marketing
Salary: 4500
*/

Go Maps

  • Map用于将数据值存储在键:值对中。
  • Map中的每个元素都是一个键:值对。
  • Map是一个无序且可更改的集合,不允许重复。
  • Map的长度是其元素的数量。您可以使用len()函数找到它。
  • Map的默认值为零。
  • Map包含对基础哈希表的引用。

Go有多种创建Map的方法。

使用 var:= 创建 Maps

语法

var a = map[KeyType]ValueType{key1:value1, key2:value2,...}
b := map[KeyType]ValueType{key1:value1, key2:value2,...}

示例

此示例显示如何在Go中创建maps。注意代码中的顺序和输出中的顺序:

package main
import ("fmt")

func main() {
  var a = map[string]string{"brand": "Ford", "model": "Mustang", "year": "1964"}
  b := map[string]int{"Oslo": 1, "Bergen": 2, "Trondheim": 3, "Stavanger": 4}

  fmt.Printf("a\t%v\n", a)
  fmt.Printf("b\t%v\n", b)
}
/*
a   map[brand:Ford model:Mustang year:1964]
b   map[Bergen:2 Oslo:1 Stavanger:4 Trondheim:3]
*/

使用 make() 函数创建 Maps

语法

var a = make(map[KeyType]ValueType)
b := make(map[KeyType]ValueType)

示例

package main
import ("fmt")

func main() {
  var a = make(map[string]string) // The map is empty now
  a["brand"] = "Ford"
  a["model"] = "Mustang"
  a["year"] = "1964"
                                 // a is no longer empty
  b := make(map[string]int)
  b["Oslo"] = 1
  b["Bergen"] = 2
  b["Trondheim"] = 3
  b["Stavanger"] = 4

  fmt.Printf("a\t%v\n", a)
  fmt.Printf("b\t%v\n", b)
}
/*
a   map[brand:Ford model:Mustang year:1964]
b   map[Bergen:2 Oslo:1 Stavanger:4 Trondheim:3]
*/

创建空 Map

有两种方式可以创建空 map。一种是使用 make() 函数,另一种是使用如下语法:

var a map[KeyType]ValueType
Note: make()函数是创建空映射的正确方法。如果您以不同的方式制作一个空映射并对其进行写入,则会导致运行时死机。
package main
import ("fmt")

func main() {
  var a = make(map[string]string)
  var b map[string]string

  fmt.Println(a == nil)
  fmt.Println(b == nil)
}
/*
false
true
*/

允许的映射键类型

映射键可以是定义了等于运算符(==)的任何数据类型。其中包括::

  • Booleans
  • Numbers
  • Strings
  • Arrays
  • Pointers
  • Structs
  • Interfaces (as long as the dynamic type supports equality)

无效的映射键类型:

  • Slices
  • Maps
  • Functions

因为这些类型内没有定义没有等于运算符(==) 。

允许的值类型

映射值可以是任何类型。

访问 Map 元素

通过key访问,语法如下:

value = map_name[key]

示例如下:

package main
import ("fmt")

func main() {
  var a = make(map[string]string)
  a["brand"] = "Ford"
  a["model"] = "Mustang"
  a["year"] = "1964"

  fmt.Printf(a["brand"])
}
/*
Ford
*/

更新和添加 Map 元素

语法

通过以下方式更新或添加元素:

map_name[key] = value

示例

package main
import ("fmt")

func main() {
  var a = make(map[string]string)
  a["brand"] = "Ford"
  a["model"] = "Mustang"
  a["year"] = "1964"

  fmt.Println(a)

  a["year"] = "1970" // Updating an element
  a["color"] = "red" // Adding an element

  fmt.Println(a)
}
/*
map[brand:Ford model:Mustang year:1964]
map[brand:Ford color:red model:Mustang year:1970]
*/

删除 Map 元素

语法

使用delete()函数删除元素:

delete(map_name, key)

示例

package main
import ("fmt")

func main() {
  var a = make(map[string]string)
  a["brand"] = "Ford"
  a["model"] = "Mustang"
  a["year"] = "1964"

  fmt.Println(a)

  delete(a,"year")

  fmt.Println(a)
}
/*
map[brand:Ford model:Mustang year:1964]
map[brand:Ford model:Mustang]
*/

检查 Map 中的特定元素

语法

可以使用以下方法检查映射中是否存在某个 Key:

val, ok :=map_name[key]

如果只想检查某个 key 是否存在,可以用空白标识符(_)代替val。

示例

package main
import ("fmt")

func main() {
  var a = map[string]string{"brand": "Ford", "model": "Mustang", "year": "1964", "day":""}

  val1, ok1 := a["brand"] // Checking for existing key and its value
  val2, ok2 := a["color"] // Checking for non-existing key and its value
  val3, ok3 := a["day"]   // Checking for existing key and its value
  _, ok4 := a["model"]    // Only checking for existing key and not its value

  fmt.Println(val1, ok1)
  fmt.Println(val2, ok2)
  fmt.Println(val3, ok3)
  fmt.Println(ok4)
}
/*
Ford true
 false
 true
true
*/
Note: 如何 key 不存在,返回的值会是空字符串 ''

Maps Are References

映射是对哈希表的引用。

如果两个映射变量引用同一个哈希表,则更改一个变量的内容会影响另一个的内容。

package main
import ("fmt")

func main() {
  var a = map[string]string{"brand": "Ford", "model": "Mustang", "year": "1964"}
  b := a

  fmt.Println(a)
  fmt.Println(b)

  b["year"] = "1970"
  fmt.Println("After change to b:")

  fmt.Println(a)
  fmt.Println(b)
}
/*
map[brand:Ford model:Mustang year:1964]
map[brand:Ford model:Mustang year:1964]
After change to b:
map[brand:Ford model:Mustang year:1970]
map[brand:Ford model:Mustang year:1970]
*/

Maps 遍历

可以使用 range 对 map 进行遍历:

package main
import ("fmt")

func main() {
  a := map[string]int{"one": 1, "two": 2, "three": 3, "four": 4}

  for k, v := range a {
    fmt.Printf("%v : %v, ", k, v)
  }
}
/*
two : 2, three : 3, four : 4, one : 1,
*/

按特定顺序遍历 Maps

映射是无序的数据结构。如果需要按特定顺序迭代映射,则必须有一个单独的数据结构来指定该顺序。

package main
import ("fmt")

func main() {
  a := map[string]int{"one": 1, "two": 2, "three": 3, "four": 4}

  var b []string             // defining the order
  b = append(b, "one", "two", "three", "four")

  for k, v := range a {        // loop with no order
    fmt.Printf("%v : %v, ", k, v)
  }

  fmt.Println()

  for _, element := range b {  // loop with the defined order
    fmt.Printf("%v : %v, ", element, a[element])
  }
}

/*
two : 2, three : 3, four : 4, one : 1,
one : 1, two : 2, three : 3, four : 4,
*/