10.Go复合类型-切片

前言

在上一章节,我们已经学习了 Go符合类型 - 数组,本章节我们来看看 切片。

2:切片

2.1 切片(slice)概念

在讲解切片(slice)之前,大家思考一下数组有什么问题?

第一:数组定义完,长度是固定的。

例如:

var num [5]int = [5]int{1, 2, 3, 4, 5}
fmt.Println(num)

定义的num数组长度是5,表示只能存储5个整型数字,现在向数组num中追加一个数字,这时会出错。

第二:使用数组作为函数参数进行传递时,如果实参为5个元素的整型数组,那么形参也必须5个元素的整型数组,否则出错。

针对以上两个问题,可以使用切片来进行解决。

切片:切片与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大,所以可以将切片理解成“动态数组”,但是,它不是数组。

2.2 切片与数组区别

通过定义,来比较一下切片与数组的区别

(1) 先回顾数组的基本定义初始化:

a := [5]int{}

数组中[ ]是一个固定的数字,表示长度。定义完后,长度是固定,最多存储5个数字。

(2)切片的基本定义初始化如下:

s := []int{}//定义空切片

看定义的方式,发现与数组很相似,但是注意:切片中的[ ]是空的,或者是“…”.切片的长度和容量可以不固定。

现在通过程序演示,动态向切片中追加数据

// 初始化切片
s := []int{1, 2, 3}
// 通过append函数向切片末尾追加数据
s = append(s, 5, 6, 7)
fmt.Println(s) // [1 2 3 5 6 7]

append( )函数,第一个参数表示向哪个切片追加数据,后面表示具体追加的数据。

最终输出结果为:

[1 2 3 5 6 7]

2.3 切片其它定义方式

(3)切片第二种定义方式:

var s1 []int //声明切片和声明数组一样,只是少了长度,此为空(nil)切片

(4)切片第三中定义方式,通过make( )函数实现

//借助make函数, 格式 make(切片类型, 长度, 容量)
s := make([]int, 5, 10) // len 长度为 5, cap 容量为 10
fmt.Println(s)
fmt.Println("len=", len(s))
fmt.Println("cap=", cap(s))

执行如下:

[0 0 0 0 0]
len= 5
cap= 10

什么是切片的长度与容量?

  • 长度是已经初始化的空间(以上切片s初始空间默认值都是0)
  • 容量是已经开辟的空间,包括已经初始化的空间和空闲的空间

我们可以通过如下图来理解切片的长度与容量:

10.Go复合类型-切片_数据

该切片的长度是5(存有数据,注意如果没有赋值,默认值都是0),容量是10,只不过有5个空闲区域。

即使没有给切片s赋值,int类型的切片 初始化的空间(长度)默认存储的数据都是0。

  • 演示如下:
s := make([]int, 5, 8) // len 长度为 5, cap 容量为 8
fmt.Println(s)
  • 输出的结果是:
[0 0 0 0 0]

在使用make( )函数定义切片时,一定要注意,切片长度要小于容量,例如:

s := make([]int, 10, 5) 是错误的。

make( )函数中的容量参数是可以省略掉的,如:

s := make([]int, 10) // len 长度为 10,容量省略则是与 len 一致

这时长度与容量是相等的,都是10.

GO语言提供了相应的函数来计算切片的长度与容量。

s := make([]int, 5, 10) // len 长度为 5, cap 容量为 10
fmt.Println("长度len=", len(s))
fmt.Println("容量cap=", cap(s))

接下来给切片s赋值,可以通过下标的方式直接来进行赋值。如下所示:

s := make([]int, 5, 10) // len 长度为 5, cap 容量为 10
s[0] = 1 // 通过下标赋值
s[1] = 2

也可以通过循环的方式来进行赋值。

s := make([]int, 5, 10) // len 长度为 5, cap 容量为 10
for i := 0; i < len(s); i++ {
s[i] = i
}

在这里一定要注意,循环结束条件是小于切片的长度,而不是容量。因为,切片的长度是指的是初始化的空间。以下方式会出现异常错误。

10.Go复合类型-切片_数组_02

给切片赋完值后,怎样将切片中的数据打印出来呢?

  • 第一种方式:直接通过下标的方式输出,例如:s[0],s[1]…。
  • 第二种方式:通过循环的方式,注意循环结束的条件,也是小于切片的长度,如下所示:
for i := 0; i < len(s); i++  {
fmt.Println(s[i])
}
  • 或者使用range方式输出:
for k,v := range s{
fmt.Printf("下标:%d, 值: %d \n", k, v)
}

2.4 切片截取

上一小节中,已经完成了切片的定义,赋值等操作,接下来看一下关于切片的其它操作。首先说一下切片的截取操作,所谓截取就是从切片中获取指定的数据。

我们通过如下程序给大家解释一下:

//定义了切片,并且完成初始化
s := []int{10, 20, 30, 0, 0}

//从切片中截取数据
slice := s[0:3:5]
fmt.Println(slice)

以上程序输出结果:

[10 20 30]

切片截取的说明:

s[0:3:5]是什么意思呢?
我们可以使用s[low:high:max]来表示
第一个数(low)表示下标的起点(从该位置开始截取),如果low取值为0表示从第一个元素开始截取,也就是对应的切片s中的10
第二个数(high)表示取到哪结束,也就是下标的终点(不包含该位置),3表示取出下标是0,1,2的数据(10,20,30),不包括下标为3的数据,那么也就是说取出的数据长度是3. 可以根据公式:3-0 计算(len=high-low),也就是第二个数减去第一个数,差就是数据长度。在这里可以将长度理解成取出的数据的个数。
第三个数用来计算容量,所谓容量:是指切片目前可容纳的最多元素个数。通过公式5-0计算(cap=max-low),也就是第三个数据减去第一个数。该案例中容量为5

案例说明

现在将以上程序进行修改:

//定义了切片,并且完成初始化
s := []int{10, 20, 30, 40, 50}

//从切片中截取数据
slice := s[0:3:5]
fmt.Println(slice)

结果是:

[10 20 30]

因为起点还是0,终点还是3.长度是3,容量是5。

继续修改该程序:

//定义了切片,并且完成初始化
s := []int{10, 20, 30, 40, 50}

//从切片中截取数据
//slice := s[0:3:5]
slice := s[0:4:5] // [low:high:max] 修改截取到下标4,不包含下标4的数据50
fmt.Println(slice)

结果是:

[10 20 30 40]

继续修改该程序

//定义了切片,并且完成初始化
s := []int{10, 20, 30, 40, 50}

//从切片中截取数据
//slice := s[0:3:5]
//slice := s[0:4:5] // [low:high:max] 修改截取到下标4,不包含下标4的数据50
slice := s[1:4:5] // [low:high:max] 修改起点为下标1,截取到下标4 = [1,4) 的数据
fmt.Println(slice)

slice切片结果是:

[20 30 40]

那么容量是多少呢?容量为4,通过第三个数减去第一个数(5-1)计算。

通过画图的方式来表示slice切片中的容量。

10.Go复合类型-切片_初始化_03

通过上面的图,可以发现切片s经过截取操作以后,将结果赋值给切片slice后,长度是3,容量是4,只不过有一块区域是空闲的。

关于切片的截取还有其它的操作

如下图所示:

操作

含义

​s[n]​

切片s中索引位置为n的项

​s[:]​

从切片s的索引位置0到len(s)-1处所获得的切片

​s[low:]​

从切片s的索引位置low到len(s)-1处所获得的切片

​s[:high]​

从切片s的索引位置0到high处所获得的切片,len=high

​s[low:high]​

从切片s的索引位置low到high处所获得的切片,len=high-low

​s[low:high:max]​

从切片s的索引位置low到high处所获得的切片,len=high-low,cap=max-low

​len(s)​

切片s的长度,总是<=cap(s)

​cap(s)​

切片s的容量,总是>=len(s)

下面通过一个案例,演示一下:

(1)​​s[:]​​ 不指定容量和长度截取,获取的截取切片与原来的切片一样
array := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := array[:] // 切片截取: 不指定容量和长度一样
fmt.Println("s1=", s1)
fmt.Printf("len = %d, cap = %d\n", len(s1), cap(s1))

执行如下:

s1= [0 1 2 3 4 5 6 7 8 9]
len = 10, cap = 10
(2)​​s[low:]​​ 从下标 low 开始截取,到结尾
array := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := array[3:] // 从下标 3 开始截取,到结尾
fmt.Println("array=",array)
fmt.Println("s1=", s1)
fmt.Printf("len = %d, cap = %d\n", len(s1), cap(s1))

执行如下:

array= [0 1 2 3 4 5 6 7 8 9]
s1= [3 4 5 6 7 8 9]
len = 7, cap = 7
(3)​​s[:high]​​从下标 0 开始,取到下标 high 的值,长度是 high,容量是原来切片的容量
array := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := array[:6] // 从下标 0 开始,取到下标 6 的值,长度是 10,容量是原来切片的容量
fmt.Println("array=",array)
fmt.Println("s1=", s1)
fmt.Printf("len = %d, cap = %d\n", len(s1), cap(s1))

执行如下:

array= [0 1 2 3 4 5 6 7 8 9]
s1= [0 1 2 3 4 5]
len = 6, cap = 10
(4) ​​s[low:high]​​从下标 low 开始,取到下标 high 的值,长度是 (high - low) , 容量为 (s 切片的容量 - low)
ar := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := ar[2:5] // 从下标 2 开始,取到下标 5 的值
fmt.Println("ar=",ar)
fmt.Println("s1=", s1)
fmt.Printf("len = %d, cap = %d\n", len(s1), cap(s1))

执行如下:

ar= [0 1 2 3 4 5 6 7 8 9]
s1= [2 3 4]
len = 3, cap = 8

​ar[2:5]​​​ 表示从下标为 2 的元素(包含该元素)开始取,到下标为 5 的元素(不包含该元素)结束。所以切片 ​​s1​​ 的长度是 3。

切片 ​​s1​​​ 的容量是多少呢?是 8,根据 ​​ar​​​ 切片的容量是 10, 减去 ​​ar[2:5]​​ 中的2。

以上就是关于切片的基本操作,这些操作在以后的开发过程中会经常用到,希望大家记住基本的规律。

2.5 思考题 - 切片截取的新切片修改值之后,原来的切片的值会不会被修改?

接下来说,思考如下题,定义一个切片array,然后对该切片array进行截取操作(范围自定义),得到新的切片s1, 并修改切片s1某个元素的值。代码如下:

array := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := array[2:5] // 从下标 0 开始,取到下标 6 的值,长度是 10,容量是原来切片的容量
fmt.Println("s1=", s1)

执行如下:

s1= [2 3 4]

s1切片的结果是:[2,3,4]

因为是从下标为2的元素(包含)开始取,到下标为5的元素(不包含)结束,取出3个元素,也就是长度为3。

现在将程序进行如下修改:修改新切片s1的下标2的值

array := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := array[2:5]
s1[2] = 888 // 修改下标2的值
fmt.Println("s1=", s1)

现在程序的输出结果是:

s1= [2 3 888]

接下来输出切片array的值:查看修改切片 s1 的值后, array 的值有没有变化

array := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := array[2:5]
s1[2] = 888 // 修改下标2的值
fmt.Println("s1=", s1)
fmt.Println("ar=",array) // 打印array的值

输出的结果如下:

s1= [2 3 888]
ar= [0 1 2 3 888 5 6 7 8 9]

发现切片array中的值也发生了变化,也就是修改切片s6的值会影响到原切片array的值,下面通过画图的形式来说明其原因。

10.Go复合类型-切片_初始化_04

在这里重点要理解的是:

​s1 := array[2:5]​​​,将​​array​​​切片中的​​array[2]​​​,​​array[3]​​​,​​array[4]​​​截取作为新切片​​s1​​​,实际上是切片​​s1​​​指向了原切片​​array​​​(在这里并不是为切片​​s1​​​新建一块区域)。所以修改​​s1​​​,也会影响到​​array​​.

**下面继续修改上面的程序:根据s1创建新切片s2 **

// 修改新切片的值 会影响原来切片的值,因为都指向同一个内存空间
array := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := array[2:5]
s1[2] = 888 // 修改下标2的值
s2 := s1[2:7] // 根据s1创建新切片s2
fmt.Println("s1=", s1)
fmt.Println("s2=", s2)
fmt.Println("ar=",array) // 打印array的值

以上程序中,切片s7的值是多少?

执行如下:

s1= [2 3 888]
s2= [888 5 6 7 8]
ar= [0 1 2 3 888 5 6 7 8 9]

下面也是通过画图的形式,来解释该程序的结果:

10.Go复合类型-切片_go语言_05


继续思考, 现在在原有的程序中又加了一行,修改​​s2[2]​​的值,如下图所示:

// 修改新切片的值 会影响原来切片的值,因为都指向同一个内存空间
array := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := array[2:5]
s1[2] = 888 // 修改下标2的值
s2 := s1[2:7] // 根据s1创建新切片s2
s2[2] = 999 // 增加修改s2[2]的值
fmt.Println("s1=", s1)
fmt.Println("s2=", s2)
fmt.Println("ar=",array) // 打印array的值

最终,切片s7与原来切片array的值分别是多少?

结果如下图所示:

s1= [2 3 888]
s2= [888 5 999 7 8]
ar= [0 1 2 3 888 5 999 7 8 9]

继续思考:以上案例中,s1切片和s2切片的容量分别是多少?

array := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := array[2:5]
s1[2] = 888 // 修改下标2的值
s2 := s1[2:7] // 根据s1创建新切片s2
s2[2] = 999 // 增加修改s2[2]的值
fmt.Println("s1=", s1)
fmt.Println("s2=", s2)
fmt.Println("ar=",array) // 打印array的值
fmt.Printf("s1的容量=%d, s2的容量=%d", cap(s1), cap(s2))

执行如下:

s1= [2 3 888]
s2= [888 5 999 7 8]
ar= [0 1 2 3 888 5 999 7 8 9]
s1的容量=8, s2的容量=6

2.6 append函数的使用

在第一节中,已经给大家讲解过切片与数组很大的一个区别就是:切片的长度是不固定的,可以向已经定义的切片中追加数据。并且也给大家简单的演示过通过append的函数,在原切片的末尾添加元素。

array := []int{1, 2, 3}
array = append(array, 4)
array = append(array, 5, 6, 7)
fmt.Println(array)

执行如下:

[1 2 3 4 5 6 7]

(问题,以上案例容量的变化,将整型换成“字符串”容量的变量)

问题:可能有同学会问,如果容量不够用了,该怎么办呢?

例如有以下切片:

s := make([]int, 5, 8)

定义了切片s,长度是5,容量是8,k

s := make([]int, 5, 8)
fmt.Printf("len = %d, cap = %d\n", len(s), cap(s))

结果是:

len = 5, cap = 8

并且前面我们讲解过,长度是指已经初始化的空间,现在切片s没有赋值,但是默认值为0

验证如下所示:

s := make([]int, 5, 8)
fmt.Printf("len = %d, cap = %d\n", len(s), cap(s))
fmt.Println(s)

结果是:

len = 5, cap = 8
[0 0 0 0 0]

现在开始通过append函数追加数据,如下所示:

s := make([]int, 5, 8)
s = append(s, 1) // 追加数据
fmt.Printf("len = %d, cap = %d\n", len(s), cap(s))
fmt.Println(s)

输出结果是:

len = 6, cap = 8
[0 0 0 0 0 1]

从输出的结果上,我们完全能够体会到,append函数的作用是在末尾追加(直接在默认值后面追加数据),由于追加了一个元素,所以长度为6.

但是如果我们把程序修改成如下所示:

s := make([]int, 5, 8)
//s = append(s, 1) // 追加数据
s[0] = 1 // 修改下标0的数据
fmt.Printf("len = %d, cap = %d\n", len(s), cap(s))
fmt.Println(s)

输出结果是:

len = 5, cap = 8
[1 0 0 0 0]

由于s[0]=1是直接给下标为0的元素赋值,并不是追加,所以结果的长度不变。

下面我们继续通过append( )继续追加数据:

s := make([]int, 5, 8)
s = append(s, 1) // 追加数据
s = append(s, 2) // 追加数据
s = append(s, 3) // 追加数据
fmt.Printf("len = %d, cap = %d\n", len(s), cap(s))
fmt.Println(s)

结果是:

len = 8, cap = 8
[0 0 0 0 0 1 2 3]

追加完成3个数据后,长度变为了8,与容量相同。

那么如果现在通过append( )函数,继续向切片s中继续追加一个数据,那么容量会变为多少呢?

代码如下:

s := make([]int, 5, 8)
s = append(s, 1) // 追加数据
s = append(s, 2) // 追加数据
s = append(s, 3) // 追加数据
s = append(s, 4) // 追加数据
fmt.Printf("len = %d, cap = %d\n", len(s), cap(s))
fmt.Println(s)

输出的结果是:

len = 9, cap = 16
[0 0 0 0 0 1 2 3 4]

追加完成一个数据后,长度变为9,大于创建切片s时的容量,所以切片s扩容,变为16.

那么切片的容量是否是以2倍容量来进行扩容的呢?

我们可以来验证一下:

//如果超过原来的容量,通常以2倍容量扩容
s := make([]int, 0, 1)
oldCap := cap(s)
for i := 0; i < 20; i++ {
s = append(s, i)
newCap := cap(s)
if oldCap < newCap {
fmt.Printf("cap: %d ==> %d\n", oldCap, newCap)
oldCap = newCap
}
}

输出结果是:

cap: 1 ==> 2
cap: 2 ==> 4
cap: 4 ==> 8
cap: 8 ==> 16
cap: 16 ==> 32

通过以上结果分析,发现是2倍的容量进行扩容。

但是我们修改一下循环条件看一下结果,将循环结束的条件修改的大一些,如下所示:

10.Go复合类型-切片_go语言_06

对应的结果:

cap: 128 ==> 256
cap: 256 ==> 512
cap: 512 ==> 1024
cap: 1024 ==> 1280 # 从1024开始就不是2倍扩容了
cap: 1280 ==> 1696
cap: 1696 ==> 2304
cap: 2304 ==> 3072
cap: 3072 ==> 4096
...

通过以上的运行结果分析:当容量小于1024时是按照2倍容量扩容,当大于等于1024是不是按照2倍容量扩容。

2.7 copy 函数使用

针对切片操作常用的方法除了append( )方法以外,还有copy方法.

基本语法:copy(切片1,切片2)

将第二个切片里面的元素,拷贝到第一个切片中。

下面通过一个案例,看一下该方法的使用:

srcSlice := []int{1, 2}
dstSlice := []int{6, 6, 6, 6, 6}

copy(dstSlice, srcSlice)
fmt.Println("dst = ", dstSlice)

上面案例中,将srcSlice中的元素拷贝到destSlice切片中。结果如下:

dst =  [1 2 6 6 6]

通过以上结果可以分析出,直接将srcSlice切片中两个元素拷贝到dstSlice元素中相同的位置。而dstSlice原有的元素备替换掉。

下面将以上程序修改一下,如下所示:

srcSlice := []int{1, 2}
dstSlice := []int{6, 6, 6, 6, 6}

//copy(dstSlice, srcSlice)
copy(srcSlice, dstSlice) // 位置做了调换
fmt.Println("src = ", srcSlice)

以上程序的结果是:

src =  [6 6]

通过以上两个程序得出如下结论:在进行拷贝时,拷贝的长度为两个slice中长度较小的长度值。

思考以下程序输出的结果:

slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}
copy(slice2, slice1)
fmt.Println("slice2=", slice2)

结果是:

slice2= [1 2 3]

现在将程序进行如下修改:

slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}
copy(slice1, slice2)
fmt.Println("slice1=", slice1)

结果是:

slice1= [5 4 3 4 5]

2.8 切片作为函数参数

切片也可以作为函数参数,那么与数组作为函数参数有什么区别呢?

接下来通过一个案例,演示一下切片作为函数参数。

package main

import "fmt"

func InitData(num []int) {
for i := 0; i < len(num); i++ {
num[i] = i
}
}

func main() {
// 创建一个切片
s := make([]int, 10)
// 初始化切片
InitData(s)
// 打印切片的内容
for _, v := range s {
fmt.Println(v)
}
}

通过以上案例,发现在主函数main( )中,定义了一个切片s,然后调用InitData( )函数,将切片s作为实参传递到该函数中,并在InitData( )函数中完成初始化,该函数并没有返回值,但是在主函数中直接打印切片s,发现能够输出对应的值。也就是在InitData( )函数中对形参切片num赋值,影响到了main( )函数中的切片s.

但是,大家仔细想一下,如果我们这里传递参数不是切片,而是数组,那么能否完成该操作呢?

那么我们将上面的程序,修改成以数组作为参数进行传递的形式:

func InitData(num [10]int) {
for i := 0; i < len(num); i++ {
num[i] = i
}
}

func main() {
// 创建一个数组
var s [10]int
// 初始化切片
InitData(s)
// 打印切片的内容
for _, v := range s {
fmt.Println(v)
}
}

发现以数组的形式作为参数,并不能完成我们的要求,所以切片作为函数实参与数组作为函数实参,进行传递时,传递的方式是不一样的。

在GO语言中,数组作为参数进行传递是值传递而切片作为参数进行传递是引用传递。

什么是值传递?什么是引用传递?

  • 值传递:方法调用时,实参数把它的值传递给对应的形式参数,方法执行中形式参数值的改变不影响实际参数的值
  • 引用传递:也称为传地址。函数调用时,实际参数的引用(地址,而不是参数的值)被传递给函数中相对应的形式参数(实参与形参指向了同一块存储区域),在函数执行中,对形式参数的操作实际上就是对实际参数的操作,方法执行中形式参数值的改变将会影响实际参数的值。

案例1:计算出一组整型数据之和。

package main

import "fmt"

func InitData(num []int) {
for i := 0; i < len(num); i++ {
fmt.Printf("请输入第%d个数: \n", i+1)
fmt.Scanf("%d", &num[i])
fmt.Scanf("%d\n", &num[i])
}
}

func SumAdd(num []int) int {
var sum int
for i := 0; i < len(num); i++ {
sum += num[i]
}
return sum
}

func main() {
var count int
var sum int
fmt.Println("请输入要计算的整型数据的个数")
fmt.Scanf("%d", &count)
s := make([]int, count) // 用输入的个数作为切片长度
InitData(s) // 完成切片初始化
sum = SumAdd(s) // 计算数据之后
fmt.Println("Sum=", sum)
}

执行如下:

请输入要计算的整型数据的个数
3
请输入第1个数:
10
请输入第2个数:
20
请输入第3个数:
30
Sum= 60

案例2:实现冒泡排序

package main

import (
"fmt"
"math/rand"
"time"
)

func BubbleSort(s []int) {
n := len(s)

for i := 0; i < n-1; i++ {
for j := 0; j < n-1-j; j++ {
if s[j] > s[j+1] {
s[j], s[j+1] = s[j+1], s[j] // 交换数据
}
}
}
}

func InitData(s []int) {
//设置种子
rand.Seed(time.Now().UnixNano())

for i := 0; i < len(s); i++ {
s[i] = rand.Intn(100) // 100以内的随机数
}
}

func main() {
n := 10
s := make([]int, n) // 创建一个切片,len为n
InitData(s) // 初始化数组
fmt.Println("排序前:", s)
BubbleSort(s) // 冒泡排序
fmt.Println("排序后:", s)
}

执行如下:

排序前: [50 76 4 49 1 78 0 79 5 76]
排序后: [1 4 49 50 76 78 0 79 5 76]

注意以上案例需要导包:

import "math/rand"   
import "time"

案例3:猜字游戏

需求:要求用户输入3位数,与系统随机产生的3位数,进行每一位比较,如果不相等,提示用户输入的数字是大了还是小了,如果相等,提示一致。最后每一位上的数字都一致,退出程序。

思路:

1:定义一个函数,随机产生三位整数

//  1:定义一个函数,随机产生三位整数
func InitData() int {
var num int

//设置种子
rand.Seed(time.Now().UnixNano())

for {
num = rand.Intn(1000) // 1000以内的随机数
if num >= 100 && num <= 999 {
break
}
}

return num
}

在这里一定要注意,对产生的随机数进行范围的判断。

2:定义一个函数,用来获取随机产生的三位整数的每一位,并且存储到切片中。

s := make([]int, 3) // 创建一个切片,len为n
GetNum(s, num)

函数GetNum( )用来获取每一位数字,并且存储到切片s中。

// 2:定义一个函数,用来获取随机产生的三位整数的每一位,并且存储到切片中。
func GetNum(randSlice []int, num int) {
randSlice[0] = num / 100 // 取百位数
randSlice[1] = num % 100 / 10 // 取个位数
randSlice[2] = num % 10 // 取十位数

}

3:定义一个方法,获取用户输入的3位整数,并调用GetNum( )获取每位数字,并且保存到切边中,接下来与系统产生的数字进行每一位上的比较。

package main

import (
"fmt"
"math/rand"
"time"
)

// 1:定义一个函数,随机产生三位整数
func InitData() int {
var num int

//设置种子
rand.Seed(time.Now().UnixNano())

for {
num = rand.Intn(1000) // 1000以内的随机数
if num >= 100 && num <= 999 {
break
}
}

return num
}

// 2:定义一个函数,用来获取随机产生的三位整数的每一位,并且存储到切片中。
func GetNum(randSlice []int, num int) {
randSlice[0] = num / 100 // 取百位数
randSlice[1] = num % 100 / 10 // 取个位数
randSlice[2] = num % 10 // 取十位数

}

//3:定义一个方法,获取用户输入的3位整数,并调用GetNum( )获取每位数字,并且保存到切边中,
//接下来与系统产生的数字进行每一位上的比较。
func OnGame(randSlice []int) { // 玩游戏
var num int
keySlice := make([]int, 3) // 保存用户输入的数字
// 不断重复比较
for {
// 获取用户输入的3位整数
for {
fmt.Println("请输入三位数:")
fmt.Scan(&num)
if num >= 100 && num <= 999 {
break
}
fmt.Println("输入的数据不符合要求")
}
// 获取用户输入的每位数字
GetNum(keySlice, num)
// 记录比较正确的次数,开始进行比较
n := 0
// 将系统产生的数字与用户输入的数字进行比较
for i := 0; i < 3; i++ {
if keySlice[i] > randSlice[i] {
fmt.Printf("第%d位大了一点\n", i+1)
} else if keySlice[i] < randSlice[i] {
fmt.Printf("第%d位小了一点\n", i+1)
} else {
fmt.Printf("第%d位猜对了\n", i+1)
n++ // 比较正确了加 1
}
}
if n == 3 {
fmt.Println("全部猜对!!!")
break // 退出循环
}
}
}

func main() {
num := InitData() // 随机产生三位数
s := make([]int, 3) // 创建一个切片,len为n
GetNum(s, num)
OnGame(s) // 开始游戏
}

执行如下:

请输入三位数:
723
第1位大了一点
第2位小了一点
第3位小了一点
请输入三位数:
87812
输入的数据不符合要求
请输入三位数:
767
第1位大了一点
第2位小了一点
第3位大了一点
请输入三位数:
111
第1位小了一点
第2位小了一点
第3位小了一点
请输入三位数:

建议:以后开发中使用切片来代替数组。