import java.io.FileNotFoundException

import scala.collection.immutable.Map

import scala.io.Source

import scala.util.Try

//用一个case类来创建学生成绩的模板,以学生成绩信息,id,gender,scores作为该样例类的参数,scores用数据结构Map封装,以课程名作为key,

//以成绩作为value

case class Student(id:String,gender:String,scores:Map[String,Int])

class Statistics_Student_Grades{

//将成绩字符串转化为数字

/**

* Try(scoreStr.toInt) 来尝试将一个字符串 scoreStr 转换为整数。Try 是 Scala 标准库中的一个类,它用于处理可能会抛出异常的代码片段。当使用 Try 对某段代码进行尝试时,如果代码执行成功(没有抛出异常),则会得到一个 Success 对象,其中包含执行结果。如果代码抛出异常,则会得到一个 Failure 对象,其中包含异常信息。

接着,在你的代码中,你调用了 getOrElse 方法。getOrElse 方法在 Try 类型中的作用类似于 Option 类型中的作用,它用于获取 Try 的值,但如果 Try 为 Failure,则会返回一个默认值。

具体解释如下:

Try(scoreStr.toInt): 这部分代码会尝试将字符串 scoreStr 转换为整数。如果转换成功,它会返回一个 Success 对象,其中包含整数值;如果转换失败,它会返回一个 Failure 对象,其中包含异常信息。

.getOrElse { throw new IllegalArgumentException(s"Invalid score: $scoreStr") }: 这部分代码是对之前尝试的结果调用 getOrElse 方法。如果尝试成功(即为 Success),它会获取成功的结果,也就是整数值。如果尝试失败(即为 Failure),它会执行传名参数 { throw new IllegalArgumentException(s"Invalid score: $scoreStr") },这段代码会抛出一个 IllegalArgumentException 异常,异常消息会包含原始的成绩字符串。

因此,这段代码的作用是尝试将 scoreStr 转换为整数。如果转换成功,它返回整数值;如果转换失败,它会抛出一个带有异常消息的 IllegalArgumentException。这种方式允许你以一种更优雅的方式处理可能的异常情况,而不是直接抛出异常

* @param scoreStr

* @return

*/

private def parseScore(scoreStr: String): Int = {

Try(scoreStr.toInt).getOrElse {

throw new IllegalArgumentException(s"Invalid score: $scoreStr")

}

}

//解析成绩清单为学生对象集合函数,参数为学生成绩单的行所构成的字符串列表,返回值为学生对象所构成的列表,该列表每一个元素都包含一名学生的成绩清单信息

def parse_ReportCard(list: List[String]):List[Student]={

//取出list中除第一个元素的所有元素,通过map函数将list中的字符串转化为学生对象,文件的一行对应一个学生对象

list.tail.map{e =>

/**

* //通过模式匹配,将右边字符串列表的值分别赋值给id,gender,math,english,physics

val List(id,gender,math,english,physics)=e.split(",").toList

//将各科分数存于Map中并赋值给scores

val scores = Map("Math"->math.toInt,"English"->english.toInt,"Physics"->physics.toInt)

//返回一个学生对象,前面两部都是为了求的这个学生对象的参数

Student(id,gender,scores)

*/

val fields = e.split(",").toList

//检验表格模式是匹配,若不匹配则抛出异常

try {

if (fields.length >= 5) {

val id :: gender :: mathStr :: englishStr :: physicsStr :: _ = fields

val math = parseScore(mathStr)

val english = parseScore(englishStr)

val physics = parseScore(physicsStr)

val scores = Map("Math" -> math, "English" -> english, "Physics" -> physics)

Student(id, gender, scores)

} else {

throw new IllegalArgumentException(s"Invalid data: $e")

}

} catch {

case _: MatchError =>

println(s"Error: Invalid data format in '$e'")

Student("", "", Map.empty[String, Int])

}

}

}

//统计各门课程的平均成绩,最低成绩,最高成绩函数,以学生对象列表作为参数,包含各科的平均成绩,最低成绩,最高成绩的Map作为返回值,以课程为key,包含对应平均成绩,

// 最高成绩,最低成绩的元组作为value。

def calculate_Average_Min_Max(students: List[Student]):Map[String, (Double, Int, Int)]={

//计算成绩的三种值,只与成绩有关,通过flatMap操作将成绩取出,再通过groupBy操作对各科成绩按课程名称进行分组,再通过map操作进行计算

students.flatMap(x=>x.scores).groupBy(x =>x._1).map{

//通过模式匹配,课程对应分数

case (course, scores) => {

//通过map函数取出分数的值的列表

val values = scores.map(x=>x._2)

//先求和,再将和转化为Double类型,再求平均值

val average = values.sum.toDouble/values.length

//最低成绩

val min = values.min

//求最高成绩

val max = values.max

//返回一个课程对应分数映射条目

course->(average,min,max)

}

}

}

//按性别统计各门课程的平均成绩,最低成绩,最高成绩

def calculate_Average_Min_Max_ByGender(list:List[Student]):Map[String,Map[String,(Double,Int,Int)]]={

list.groupBy(x=>x.gender).map{case(gender,list1)=>{

val list2 = calculate_Average_Min_Max(list1)

gender->list2

}}

}

//打印各门课程的平均成绩,最低成绩,最高成绩

def print_ScoresInformation(map1:Map[String,(Double,Int,Int)])={

println("平均成绩\\最低成绩\\最高成绩")

map1.foreach(x=>println(x+" "))

println()

}

//按性别打印各门课程的平均成绩,最低成绩和最高成绩

def print_ScoresInformation_ByGender(map2:Map[String,Map[String,(Double,Int,Int)]])={

map2.foreach { case (gender, courseStats) =>

println(s" ($gender) course\taverage\tmin\tmax")

courseStats.foreach { case (course, (average, min, max)) =>

println(s"$course:\t${f"$average%.2f"}\t$min\t$max")

}

}

}

def read_ReportCard(filepath: String): List[String] = {

try {

val source = Source.fromFile(filepath)

val lines = source.getLines().toList

source.close()

lines

} catch {

case _: FileNotFoundException =>

println(s"Error: File not found at '$filepath'")

List.empty[String]

}

}

}

//创建一个单例对象,用来实现main函数的封装

object Statistics_Student_Grades {

val statistics_Student_Grades = new Statistics_Student_Grades

def main(args: Array[String]): Unit = {

//学生成绩单文件路径

val filepath = "H:\\data\\学生成绩表二.csv"

//读取学生成绩

val lines = statistics_Student_Grades.read_ReportCard(filepath)

if(lines.nonEmpty){

try{

//解析成绩单为学生对象的集合

val students = statistics_Student_Grades.parse_ReportCard(lines)

//打印各门课程的平均成绩,最低成绩和最高成绩

val tongjichengji = statistics_Student_Grades.calculate_Average_Min_Max(students)

statistics_Student_Grades.print_ScoresInformation(tongjichengji)

//按性别打印各门课程的平均成绩,最低成绩,最高成绩

val anxingbie = statistics_Student_Grades.calculate_Average_Min_Max_ByGender(students)

statistics_Student_Grades.print_ScoresInformation_ByGender(anxingbie)

} catch {

case e: IllegalArgumentException => println(s"Error: ${e.getMessage}")

}

}

}

}

运行结果:

 

在这段代码中,你使用了 `Try(scoreStr.toInt)` 来尝试将一个字符串 `scoreStr` 转换为整数。`Try` 是 Scala 标准库中的一个类,它用于处理可能会抛出异常的代码片段。当使用 `Try` 对某段代码进行尝试时,如果代码执行成功(没有抛出异常),则会得到一个 `Success` 对象,其中包含执行结果。如果代码抛出异常,则会得到一个 `Failure` 对象,其中包含异常信息。

接着,在你的代码中,你调用了 `getOrElse` 方法。`getOrElse` 方法在 `Try` 类型中的作用类似于 Option 类型中的作用,它用于获取 `Try` 的值,但如果 `Try` 为 `Failure`,则会返回一个默认值。

1. Try(scoreStr.toInt): 这部分代码会尝试将字符串 `scoreStr` 转换为整数。如果转换成功,它会返回一个 `Success` 对象,其中包含整数值;如果转换失败,它会返回一个 `Failure` 对象,其中包含异常信息。

2. .getOrElse { throw new IllegalArgumentException(s"Invalid score: $scoreStr") }: 这部分代码是对之前尝试的结果调用 `getOrElse` 方法。如果尝试成功(即为 `Success`),它会获取成功的结果,也就是整数值。如果尝试失败(即为 `Failure`),它会执行传名参数 `{ throw new IllegalArgumentException(s"Invalid score: $scoreStr") }`,这段代码会抛出一个 `IllegalArgumentException` 异常,异常消息会包含原始的成绩字符串。

因此,这段代码的作用是尝试将 `scoreStr` 转换为整数。如果转换成功,它返回整数值;如果转换失败,它会抛出一个带有异常消息的 `IllegalArgumentException`。这种方式允许你以一种更优雅的方式处理可能的异常情况,而不是直接抛出异常。

这个错误提示是因为在你的代码中,你可能在不同的上下文中使用了可变和不可变的 `Map`。Scala 的标准库中有两个 `Map` 类,一个是 `scala.collection.immutable.Map`(不可变),另一个是 `scala.collection.mutable.Map`(可变)。你的代码中可能出现了不同类型的 `Map`,导致类型不匹配。

要解决这个问题,确保你在整个代码中使用一致的 `Map` 类型。如果你想使用不可变的 `Map`,则需要在代码中的所有地方都使用 `scala.collection.immutable.Map`。如果你想使用可变的 `Map`,则需要使用 `scala.collection.mutable.Map`。

例如,假设你想要使用不可变的 `Map`,你可以这样修改代码:

```scala import scala.collection.immutable.Map

def calculateCourseStatsByGender(students: List[Student]): Map[String, Map[String, (Double, Int, Int)]] = {   val statsByGender = students.groupBy(_.gender).map {     case (gender, genderStudents) =>       val courseStats = calculateCourseStats(genderStudents)       gender -> courseStats   }      statsByGender } ```

确保你在整个代码中都使用了 `scala.collection.immutable.Map`。如果你想使用可变的 `Map`,则将 `import scala.collection.immutable.Map` 替换为 `import scala.collection.mutable.Map` 并在适当的地方使用 `mutable.Map`。

参考链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: