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`。
参考链接
发表评论