Go语言实践[回顾]教程11--学习成绩统计的示例【下】

  • 需求未变但源数据结构改为 map 类型
    • 创建 map 类型的数据源文件
    • 创建操作 map 类型数据进行统计排序的源文件
  • 本节小结

需求未变但源数据结构改为 map 类型

  在上两节中使用的源数据都是仅有分数没有姓名的一维数组,但实际成绩单中基本都是姓名与分数对照形式出现的,这就与 Go 语言的 map 类型十分相似,再者从数据库取出来的也几乎都是与 map 类型一致,所以,这节我们以 map 类型为源数据,重新完成项目需求。

创建 map 类型的数据源文件

  使用前面的方法在本项目 data 目录下创建一个 map_score.go 文件,更新为如下代码:

// 姓名、分数键值对格式的成绩的数据,文件名 map_score.go
package datavar MapScore = map[string]int{"张三丰": 86,"郭大壮": 67,"李晓明": 73,"王二虎": 98,"赵慧兰": 90,"丁菲菲": 56,"孙艳玲": 49,"周喜庆": 77,"高建国": 89,"吴亚芬": 96,
}

  这个格式其实与其他很多语言类似,在 Go 语言中叫 map(映射),其实与 PHP、JavaScript 中的键值对数组、Python 中的字典都很相似。核心特点就是键值对,元素是一组有键(Key)有值(Value)的结构。

  第4行,var MapScore = map[string]int 表示声明定义一个键是字符串类型,值是整数类型的映射类型变量 MapScore。

  注意第4行的变量名 MapScore 首字母一定要大写,否则其他包无法使用。

创建操作 map 类型数据进行统计排序的源文件

  使用前面的方法在本项目 count 目录下创建一个 map_data_count.go 文件,更新为如下代码:

// 统计来自 map 类型的数据,文件名 map_data_count.go
package countimport ("fmt""score_count/data""sort"
)// MapDataCount  对一维数组进行排序、统计60分及以上人数,前三名分数
func MapDataCount() {// 获取数组长度赋值给 len 以免后面多处使用重复计算len := len(data.OnlyScore)// 声明一个新的结构体数据结构类型 studenttype student struct {Name  stringScore int}// 声明一个空切片,用于保存正序结果var positiveArr []student// 将源 map 内的数据全部以 student 结构复制到切片 positiveArr 中for k, v := range data.MapScore {positiveArr = append(positiveArr, student{k, v})}// 使用标准库提供的正序排序函数给切片 positiveArr 排序sort.Slice(positiveArr, func(i, j int) bool {return positiveArr[i].Score < positiveArr[j].Score // 通过判断决定升序})// 创建一个与源数据数组同样长同样类型的切片,用于存放倒序结果reverseArr := make([]student, len)passNum := 0 // 用于保存及格人数levelA, levelB, levelC, levelD := 0, 0, 0, 0// 将排序好的数组颠倒顺序,使用for循环综合性能更好// 统计及格数量刚好也要用for循环,干脆放在一个排序后的循环里for i := 0; i < len; i++ {v := positiveArr[i].ScorereverseArr[len-i-1] = positiveArr[i]//     // 利用正序排列特点,综合判断统计各分数段if levelD == 0 && v >= 60 {passNum = len - ilevelD = i} else if levelC == 0 && v >= 75 {levelC = i - levelD} else if levelB == 0 && v >= 90 {levelB = i - levelC - levelDlevelA = len - i}}// 对倒序排序后的成绩数组直接切片前三个元素就刚好是前三名的成绩topThree := reverseArr[:3]fmt.Println("及格人数:", passNum)fmt.Printf("及格率为: %d%%\n", passNum*100/len)fmt.Println("前三分数:", topThree)fmt.Println("成绩正序:", positiveArr)fmt.Println("成绩倒序:", reverseArr)fmt.Println("A 级人数:", levelA)fmt.Println("B 级人数:", levelB)fmt.Println("C 级人数:", levelC)fmt.Println("D 级人数:", levelD)
}

  以上代码运行结果正确。

  由于整体代码逻辑与上一节的相同,只是更换的了数据的格式,所以这里仅针对处理与 map 相关的变动做说明。

  第16~19行:通过结构体声明一个自定义的数据结构类型,一个结构体内可以有不同类型的成员。
  第16行:type 是声明类型的关键字;student 自定义的类型名;struct 声明结构体的关键字,表示是多个数据的集合。
  第17行:Name 表示结构体中的一个成员名称叫 Name;string 表示成员 Name 的数据类型为字符串。
  第18行:Score 表示结构体中的一个成员名称叫 Score;int 表示成员 Score 的数据类型为整数。
  注意,成员名的首字母大写,结构体外部才可以使用这个成员。

  因为 map 类型不支持直接排序操作,所以要先把 map 类型的源数据转成切片才可以使用 sort 包排序。要转成切片(本质是数组),需要先把 map 的键值对封装成一个新的类型,才可以作为一个元素追加到切片中。这就是为什么要先声明一个结构体的原因。

  第22行:声明了一个 student 类型的切片 positiveArr,用于保存从源数据 map 中复制过来的数据(以新建的数据类型 student 添加的),然后会给这个切片排序(正序)。

  第25~27行:将 map 中的源数据循环添加到 positiveArr 切片中。
  第25行:在 for 循环中使用 range 关键字遍历 data.MapScore 源数据 map,每次都返回 map 的键和值分别赋值给 k 和 v。
  第26行:在循环体中,使用 append() 方法,依次向 positiveArr 切片尾部追加 student 结构类型的数据,数据内容是 student 的 Name 成员的值是循环得到 map 元素的键名 k,student 的 Score 成员的值是循环得到 map 元素的值 v。然后返回一个新的切片给 positiveArr。
  循环结束后,positiveArr 切片的内部结构其实就是可以看做一个 student 类型的一维数组了。

  第30~32行:使用标准库中的 sort.Slice() 函数给 positiveArr 切片排序。
  第30行:sort.Slice() 函数的功能是自定义排序。第一个参数是待排序的切片;第二个参数是个排序判断方法的匿名函数,返回真或假,表示两个元素是否交换位置。其中匿名函数中的 i 表示后一个元素(当前)索引,j 表示前一个元素索引。
  第31行:表示返回 当前元素(结构体)的 Score 成员值是否大于上一元素的 Score 成员值 的关系判断结果(真或假),决定是否交换元素内容,就等于是否更改排列顺序。

  第35行:创建一个 student 类型的切片,长度与源数据 map 相同。

  第44行:切片 positiveArr 当前元素的 Score 成员才是分数,因为当前元素是个结构体了。

本节小结

  以下是对本节涉及的 Go 语言编程内容的归纳总结,方便记忆:

  ● map,是引用的集合类型,其元素是成对出现的,也就是键值对,一个键名对应一个值。其实也可以理解为是一个较特殊的数组,只不过它的索引是键名,不是顺序号。map 是无序排列的,所以不能用顺序号取值。
  var 变量名 map[键类型]值类型 是 map 的典型声明格式。声明时不需要知道 map 的长度,因为 map 是可以动态增长的,未初始化的 map 的值是 nil,len() 函数可以获取 map 中 键值对 的数量。
  var m map[string]int 表示声明一个键是字符串值是整数的 map 变量 m。
  var m = map[string]int{“A”:25, “B”:30} 表示声明一个键是字符串值是整数的 map 变量 m,并初始化赋值两个键值对元素,A的值是25,B的值是30。这是 len(m) 的结果是 2。
  m.[“A”] 表示获取 map 变量 m 中 A 键的值,也就是 25。
  m.[“A”] = 66 表示将 66 赋值给 map 变量 m 中 A 键的值。

  ● 结构体,是带有成员的复合类型,每个成员都有自己的名字,各成员的值类型可以不同。主要用于创建一个新的类型(数据结构)。
  type tm struct {
    X string
    Y int
  }
  这是结构体的典型声明格式,tm 是结构体类型名,注意 tm 在这里代表一个数据类型了type 关键字表示要声明一个类型,struct 关键字表示定义一个结构体(数据结构)。X、Y 是自定义的成员名(不需要引号),string 是成员 X 的数据类型,int 是成员 Y 的数据类型。
  var m tm,tm不能直接当变量使用,因为它是一个数据类型,需要用它声明一个变量才可以发挥作用。所以这表示声明一个 tm 类型的变量 m。
  m.X,表示获取 m 下的成员 X 的值。
  m.X = “one”,表示将 m 下的成员 X 的值修改为 “one”。

  ● 匿名函数,就是没有函数名的函数,申明格式如下:
  func(参数1, 参数n) 返回值类型 {
  …函数体…
  }
  由于匿名函数没有函数名,无法通过名称来调用,所以通常都是直接写在需要调用的地方,如需要回调函数的地方等。

  ● sort.Slice(),是标准库 sort 包中的自定义排序函数,如何排序是通过回调函数实现的。
  func Slice(s interface{}, rule func(i, j int) bool),s 是待排序的切片数据;rule 是回调函数,通过返回布尔值决定是否调换元素位置;i 和 j 都是这个函数的参数,i 表示内部循环的当前索引,j 表示内部循环的当前索引的前一个索引。这是使用了命名函数,也就是函数声明定义在其他地方单独写。
  func Slice(s interface{}, func(i, j int) bool {}),这种格式与上面差别就是使用了匿名函数,去掉了函数名,将函数体直接写在了后面的花括号里。

  ● append(),用于为切片追加数据,每次追加的数据都是在最后一个元素的后面,将返回一个新的切片。
  append(s, “new”),表示将字符串“new”追加到切片 s 尾部。
  append(s, 1, 5, 22),表示将整数 1、5、22 追加到切片 s 尾部。
  append(s, []int{1, 5, 22}),表示将整数类型包含 1、5、22 元素的切片追加到切片 s 尾部,会自动解开按元素追加。
  要注意追加的数据类型与原切片的数据类型一致。
.
.
上一节:Go/Golang语言学习实践[回顾]教程10–学习成绩统计的示例【中】

下一节:Go/Golang语言学习实践[回顾]教程12–快速体验Go语言的并发之美
.

Go语言实践[回顾]教程11--学习成绩统计的示例【下】相关推荐

  1. Go语言实践[回顾]教程10--学习成绩统计的示例【中】

    Go语言实践[回顾]教程10--学习成绩统计的示例[中] 基于整体需求优化上节代码 需求增加按等级分类统计 本节小结 基于整体需求优化上节代码   在上一节中,是基于三个基本需求各自独立实现的逻辑,创 ...

  2. Go语言实践[回顾]教程09--学习成绩统计的示例【上】

    Go语言实践[回顾]教程09--学习成绩统计的示例[上] 在数组格式成绩数据中统计及格人数和及格率并取出前三名 创建只有成绩分数的数据源码文件 创建用于统计以 int 数组为数据源的源码文件 修改主文 ...

  3. Go语言实践[回顾]教程08--通过时间判断时辰的示例【下】

    Go语言实践[回顾]教程08--通过时间判断时辰的示例[下] 封装函数向模块化开发方向修改 使用单个返回值函数的源代码 使用多个返回值函数的源代码 使用命名返回值函数的源代码 分离文件继续模块化实践 ...

  4. Go语言实践[回顾]教程15--详解Go语言的基本数据类型

    Go语言实践[回顾]教程15--详解Go语言的基本数据类型 布尔型(bool) 整数型(int) 浮点型(float) 复数型(complex) 字符串型(string) 字符型(byte / run ...

  5. Go语言实践[回顾]教程06--通过时间判断时辰的示例【上】

    Go语言实践[回顾]教程06--通过时间判断时辰的示例[上] 示例项目的需求 实现示例需求的源代码 使用 if 判断逻辑实现的源代码 使用 if else if 判断逻辑实现的源代码 使用 switc ...

  6. Go语言实践[回顾]教程23--详解Go语言函数的声明、变参、参数传递

    Go语言实践[回顾]教程23--详解Go语言函数的声明.变参.参数传递 函数的声明(定义) 函数的基本声明格式与调用 函数的变参(不定参) 值传递还是引用地址传递   函数是 Go 语言源代码的基本构 ...

  7. Go语言实践[回顾]教程03--Go语言的编译与运行的命令行

    Go语言实践[回顾]教程03--Go语言的编译与运行的命令行 Go语言是编译型静态语言 如何编译Go语言的源文件 如何执行(运行)编译后的文件 开发中如何编译后立即执行 总结 Go语言是编译型静态语言 ...

  8. Go语言实践[回顾]教程21--详解Go语言的空值、零值、nil

    Go语言实践[回顾]教程21--详解Go语言的空值.零值.nil Go 语言中 零值.空值.nil 概念之我见 基本数据类型的零值 复合数据类型的空值 通过实例体验零值.空值 nil 的差别 nil ...

  9. C语言实践(一)实现成绩输入排序和再插入排序

    C语言实践(一) 2020-09-14 20:35:52 项目一   要求 1.定义一个数组a[11],用以存放学生的成绩. 2.从键盘输入10个学生成绩  3.采用冒泡法,将学生成绩按照从高到低进行 ...

最新文章

  1. Windows下PCL1.9.1配置(编译源码)
  2. 『中级篇』docker之CI/CD持续集成-项目生成镜像(76)
  3. 16年寒假随笔(2)
  4. 【HDOJ】1890 Robotic Sort
  5. java计算二叉树的节点最小值_java计算二叉树的高度以及叶节点个数
  6. IBM、甲骨文、CNCF 就谷歌对 Istio 治理的处理提出抗议
  7. java 死锁 解决_Java死锁故障排除和解决
  8. 【Go语言】【15】GO语言的面向对象
  9. anaconda cuda路径_anaconda+cuda+cudann+tensorflow环境安装
  10. SharePoint 2010-在ribbon上添加表单,将默认control加到自定义group中
  11. bnuoj4220素数难题
  12. 资深人士关于PIFA天线的理解与讨论
  13. 用Tableau制作10种漂亮的饼图
  14. 关于视频播放的断点续传实现(.NET)
  15. PON系统基础知识简介
  16. vue 高德地图 不同区域显示不同颜色_高德地图这样用成为你的图表神器
  17. 优维科技联合广发证券发起「CD持续交付一期项目复盘会」
  18. inet_aton函数
  19. 机器学习--模型参数优化及scoring可选参数
  20. 远程桌面控制软件 Splashtop 新增本地部署版产品 On-Prem

热门文章

  1. 拍全景为什么会需要全景云台?云台是怎么安装的?
  2. RHEL / CentOS 配置epel源
  3. Typescript教程——中文文档
  4. ContentResolver与ContentProvider的搭配使用
  5. android弹出确认对话点击取消,Android 点击AlertDialog上的确定和取消按钮,使对话框不消失...
  6. 面试遇到的问题以及解决方案1.0
  7. 网络编程----基于Java Email的电子邮件发送
  8. JAVA基础入门--个人学习总结
  9. 公司渠道总监岗位职责
  10. 运动服饰品牌Champion(R)再次推出拳王阿里胶囊系列新品