- 在数组格式成绩数据中统计及格人数和及格率并取出前三名
- 创建只有成绩分数的数据源码文件
- 创建用于统计以 int 数组为数据源的源码文件
- 修改主文件 main.go 代码调用功能包的函数
- 本节小结
在一组仅有成绩数字的数组中,统计成绩大于等于60(60及以上为及格)的人数有多少,并计算及格率,以及取出前三名的成绩分别是多少。最后打印输出统计计算结果。
创建只有成绩分数的数据源码文件继续走模块化开发之路,我们用之前的方法重新创建一个项目 score_count,然后在 LiteIDE 上做下图所示 *** 作:
鼠标点击 LiteIDE 左侧的“1:目录”,然后在“score_count”文件夹上鼠标右键,在d出菜单上鼠标点选“新建目录…”选项,将d出如下界面:
在“目录名”输入框内输入“data”,然后点击“新建”按钮,就创建了 data 文件夹(目录),用于存放数据类源文件。然后我们要在这个文件夹内创建一个存放成绩数据的 Go 源文件:
鼠标点击目录列表中 score_count 目录左侧的箭头,使 score_count 目录展开,然后就看到我们刚新建的 data 目录了,接下来我们在 data 目录上鼠标右键,在d出菜单上点选“新建文件向导…”选项,看到如下界面:
鼠标点选 Go Source File 选项,然后在名称输入框中输入 only_score ,最后点击“OK”按钮(注意位置输入框中已经自动加上了 \data 目录),d出如下询问界面:
鼠标点击“Yes”按钮,会自动打开刚创建的 only_score.go 文件,如下图:
然后我们把 only_score.go 文件内的代码修改成如下代码:
// 仅有成绩的数据,文件名 only_score.go
package data
var OnlyScore = [10]int{89, 72, 56, 95, 48, 66, 74, 98, 93, 59}
第2行,声明包名为 data,其他包(如 main 包)使用这里的数据需要带 data 包名。
第4行,声明一个数组变量 OnlyScore,注意变量名是大驼峰,首字母大写。在 Go 语言语法规则中,如果允许外部包使用的常量、变量、类型、函数名、结构体等,名称首字母必须大写,如果首字母小写,则只有本包内可以使用。相当于其他语言的公有和私有。因为是例举,没有输入太多的数据,仅初始化了10个 int 类型的数据。
创建用于统计以 int 数组为数据源的源码文件按照上面的方法,在 score_count 目录下创建一个 count 子目录,用于存放统计 *** 作的逻辑代码源文件。然后在 count 目录下创建一个 arr_data_count.go 文件,用于编写完成对只有 int 类型数组做数据来源的统计功能。
我们先整理一下思路,统计及格人数、取前三名分数、整体排序这三个需求,多可以独立实现。但是如果这三个全部都需要实现的话,有没有什么可以贯穿的技巧呢?
我们一步一步来,先把需求各自独立分析一下:统计及格人数,我们可以使用循环,遍历数组的每个元素。判断元素值是否大于等于60,然后放到一个新数组中最后统计数组元素数。若不使用及格的具体分数,也可以使用计数变量的方式;取前三名分数,也可以使用遍历元素,然后与仅有三个元素的数组每个元素比较,如果大于就更新,最后这只有三个元素的数组内就是前三名分数。整体排序,使用遍历元素的冒泡排序算法也可以,但是 Go 语言标准库给我们提供了可用的包,直接使用就可以了。
那我们就先按照独立分析的,草率的先去实践一下,将此文件更新为如下代码:
// 统计来自 int 类型数组的数据,文件名 arr_data_count.go
package count
import (
"fmt"
"score_count/data"
"sort"
)
// passCount 统计及格人数及及格率
func passCount() {
passNum := 0 // 及格人数
for i := 0; i < len(data.OnlyScore); i++ {
if data.OnlyScore[i] >= 60 {
passNum++
}
}
fmt.Printf("及格人数:%d\n", passNum)
fmt.Printf("及格率为:%d%%\n", passNum*100/len(data.OnlyScore))
}
// topThreeCount 统计前三名分数
func topThreeCount() {
var topThree [3]int // 用于保存前三名分数的整数数组,默认元素值都是 0
for _, v := range data.OnlyScore {
if v > topThree[0] {
topThree[2] = topThree[1]
topThree[1] = topThree[0]
topThree[0] = v
} else if v > topThree[1] {
topThree[1] = topThree[0]
topThree[1] = v
} else if v > topThree[2] {
topThree[2] = v
}
}
fmt.Printf("前三分数:%d,%d,%d\n", topThree[0], topThree[1], topThree[2])
}
// sort_all 对整体成绩排序
func sort_all() {
sortArr := data.OnlyScore[:] // 引用数组全部内容给切片 sortArr
// 使用标准库提供的正序排序函数
sort.Ints(sortArr)
fmt.Println("成绩正序:", sortArr)
// 使用标准库提供的倒序排序函数
sort.Sort(sort.Reverse(sort.IntSlice(sortArr)))
fmt.Println("成绩倒序:", sortArr)
}
// ArrDataCount 对一维数组进行排序、统计60分及以上人数,前三名分数
func ArrDataCount() {
passCount()
topThreeCount()
sort_all()
}
第6行,导入我们自定义的 data 包,因为要使用里面的数据变量。导入自定义包的语法,实际就是 import 自定义包的路径。这个路径的起始位置是 $GOROOT 或 $GOPATH,所以自定义的包要放在这两个路径其中的一个里面,否则会导入失败。
第7行,导入标准库中的 sort 包,Go 语言标准库为我们提供了排序相关的一些方法,我们可以直接使用其对数据进行排序,而不用自己写循环和算法。
第11~22行,声明定义 passCount 函数,用于统计及格人数及及格率。
第12行,声明变量 passNum,并初始化为 int 类型的数值 0。
第14行,for,循环关键字,这个与其他语言十分相似。i := 0; 给循环计数变量 i 赋初值 0 ;i < len(data.OnlyScore); 循环的成立的条件为变量 i 小于数组 data.OnlyScore 的长度(元素数)。len() 是计算长度的函数。data.OnlyScore 是自定义包 data 中的 OnlyScore 变量,也就是成绩数组;i++ 是表示每循环依次变量 i 自增 1。
第15行,data.OnlyScore[i] 是表示获取数组第 i 个元素,i 的值是从 0 到 9 依次变化的,每循环一次增加 1。如果当前获取的元素大于等于60说明这个分数是及格的,这个判断就成立,会执行第16行代码,否则执行下一轮循环。
第16行,当前条件成立执行此行,让及格人数变量 passNum 自增 1。
循环结束后,数组 data.OnlyScore 中的每个元素都经过了第15行的判断,那么变量 passNum 自增了几次,就是有多少个及格的。passNum 的初值又是 0,所以 passNum 的现值就等于自增的次数,也就代表了及格的人数。
第20行,fmt.Printf() 与之前使用的 fmt.Println() 函数仔细看不是同一个。fmt.Println() 打印后可以自动换行,但是不支持格式化占位符。而 fmt.Printf() 支持格式化占位符但不自动换行,如果要换行则需要在换行的位置输入“\n”。“及格人数:%d\n” 带格式化占位符的打印输出内容,%d 表示这个位置实际输出的内容是一个数字,具体值在后面对应位置参数(第一个 %d对应之后的第一个参数,依此类推),\n 表示实际输出是换行 *** 作。passNum 的值将替换到前面的 %d 后被打印输出。最终输出及格人数标题和数字。
第21行,除第二个参数是表达式外,与第20行作用相同。最终输出及格率标题和数字。
第25~42行,声明定义 topThreeCount 函数,用于统计前三名分数。
第26行,声明一个只有3个元素的 int 类型的数组 topThree,默认值都是 0。用于保存 前三名的的分数。
第28行,虽然也是个 for 循环,但是没有使用循环计数变量,而是使用了关键字 range,意思是遍历数组 data.OnlyScore(就是按照数组的元素数循环),每次取出的元素值赋值给变量 v。但是看等号左侧是 “_, v”,这是什么意思呢?因为使用 range 遍历数组,每次返回的都是两个值,第一个是元素的索引,第二个是元素的值,所以要用两个变量来接。又因为索引在这里我们没有用到,不接又会报错,所以Go语言给出了一个无名变量的声明方式,就是 “_”,其实可以理解为接到后就抛弃。
第29~38行,是循环体内的对前三名的判断逻辑代码,可理解为:
● 首先判断当前元素值 v 是否大于前三名数组 topThree 中第一个元素。如果大于则把前三名数组中的元素值依次向后移动一个位置,等于空出第一个元素的位置,然后把 v 赋值给前三名数组的第一个元素。这样前三名数组的第一个元素(topThree[0])就始终保持记录最大的值。
● 如果 v 不大于 topThree[0],再判断 v 是否大于前三名数组的第二个元素。如果大于则把前三名数组除第一个元素之外的元素依次向后移动一个位置,等于空出了第二个元素的位置,然后把 v 赋值给前三名数组的第二个元素。这样前三名数组的第二个元素(topThree[1])就始终保持记录第二大的值。
● 如果前两个条件都不成立,再判断 v 是否大于前三名数组的第三个元素。如果大于则把 v 赋值给前三名数组的第三个元素。这样前三名数组的第三个元素(topThree[2])就始终保持记录第三大的值。
变量 v 对应着成绩数组 data.OnlyScore 的每个元素,依次每循环一次代表一个元素。这样循环结束,所有的元素就都经过依次循环体内的判断逻辑,最后前三名数组 topThree 中保存的就是所有成绩中,从第一到第三的前三名分数。
第41行,使用格式化打印输出函数,输出结果。
第45~55行,声明定义 sort_all 函数,用于对整体成绩排序。
第46行,声明一个变量 sortArr 为 data.OnlyScore 数组的 切片,切片是对数组全部或局部的引用,[:] 表示引用数组的所有元素为切片。因为标准库中的 sort 包不能对数组直接排序,可以对切片直接排序,所以要把数组中的数据先转为切片形式才可以使用 sort 中的方法。
第49行,使用标准库中的 sort.Ints() 方法对切片 sortArr 进行正序排序。得到的结果是从小到大的顺序。由于切片是对数组的引用,所以排序后原始数组 data.OnlyScore 与 sortArr 的内容是一样的。
第50行,使用自动换行的 fmt.Println() 函数打印输出两个参数,输出正序排序的结果。
第53行,使用标准库 sort 中的 Sort()、Reverse()、IntSlice() 三个方法组合对切片 sortArr 进行倒序排序。得到的结果是从大到小的顺序。
第54行,使用自动换行的 fmt.Println() 函数打印输出两个参数,输出倒序排序的结果。
第58~62行,声明定义 ArrDataCount 函数,这里调用上面三个参数,并使用大写字母开头的命名格式,使其他包可以直接调用(注意,上面三个函数名都是小写字母开头,外部包是不可以直接调用的)。
修改主文件 main.go 代码调用功能包的函数// score_count 项目主文件,文件名 main.go
package main
import (
"fmt"
"score_count/count"
)
func main() {
fmt.Println("成绩统计开始!")
count.ArrDataCount()
fmt.Println("成绩统计完成!")
}
第6行,导入上面自定义的 count 包,以便可以使用其里面的 ArrDataCount 方法(函数)。这里没有直接使用 data 包,所以这里不需要导入。
第11行,直接调用 count 包里的 ArrDataCount 方法即可。包名与包内的方法、属性等在使用时的格式是:包名.函数名() 或 包名.变量名。中间的连接符是英文小数点,包名是文件内声明的包名,与导入的路径名没有直接关系。
上面 *** 作都完成后,接下来在 LiteIDE 上切换到 main.go 编辑页面,然后点击编译运行,就可以看到统计结果了,如下图:
本节小结以下是对本节涉及的 Go 语言编程内容的归纳总结,方便记忆:
● 标准库中 fmt 包常用的打印输出函数:
fmt.Println(参数1, 参数2, 参数n…),自动换行,不支持格式化占位符。可多参数打印,每个参数输出后面自动跟一个空格,自动处理参数的类型问题。
fmt.Printf(格式化表达式字符串, 变量1, 变量n…),支持格式化占位符,不自动换行,换行需要加“\n”字符串。第一个参数是准备输出的字符串表达式,里面出现的 %d 需要用10进制数值变量替换,%s 需要用字符串变量替换,%v 根据变量类型自动处理替换,%T 会被替换成类型值。后面其他参数全部是变量或字面量,顺序与表达式内的 % 占位符一一对应。
fmt.Print(变量/字面量1, 变量/字面量2, 变量/字面量n…),原样输出,不加空格也不加换行,也不支持占位符,自动处理参数的类型问题。
● 切片是对数组的一个连续片段的引用,常用的引用格式(例举 arr := [5]{1, 2, 3, 4, 5}):
s = arr[:],将数组 arr 的所有元素引用为切片 s,这时 s 与 arr 指向的实际内容是同一个,且 s 仅相当于 arr 的一个别名。
s = arr[1:3],将数组 arr 的 1号索引元素(包含1号)到 3号索引元素为止(不包含3号)生成切片,本例结果是 [2,3]。
s = arr[:3],将数组 arr 的 第1个元素(包含1号)到 3号索引元素为止(不包含3号)生成切片,本例结果是 [1,2,3]。
s = arr[1:],将数组 arr 的 1号索引元素(包含1号)到 最后一个元素生成切片,本例结果是 [2,3,4,5]。
由于切片是对数组的全部或部分引用,所以对切片中元素的修改,也会直接影响引用的数组。
● for 循环通常以循环计数变量做循环次数判断基础,但是对集合类如数组类型进行遍历时,可以不使用循环计数变量:
for i:=0; i
● import 自定义包时,实际就是 import 自定义包的路径。自定义包要在 环境变量 GOROOT 或 GOPATH 目录下,包的路径的书写起点位置也是 GOROOT 或 GOPATH 目录这里开始的。至于导入如网络资源包的其他情况后面使用到时再整理。
● 使用自定义包中的方法(函数)或属性(变量)是,格式为:自定义包名.函数名(或变量名)。这里的包名是包文件内使用 package 关键字声明的名字,与 import 的路径名没有直接关系。
.
.
.
上一节:Go/Golang语言学习实践[回顾]教程08–通过时间判断时辰的示例【下】
下一节:Go/Golang语言学习实践[回顾]教程10–学习成绩统计的示例【中】
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)