一、实验环境
Ubuntu 20.04 - VMware Workstation
java - openjdk version “1.8.0_312”
Scala code runner version 2.11.12
二、List常规用法
- 实验内容
- 定义一个列表List1,内容为(“a”, “b” ,“c”, “d”, “e”, “f”, “g”) ,在列表List1开头添加指定列表List2(“m”, “n”, “a”, “b” , “e”)的元素;
- 在(1)的基础上,去除List1列表的重复元素, 然后从指定位置 0 开始查找元素d第一次出现的位置,并输出结果;
- 对上述List1进行列表反转;
- 对上述List1按降序排序。
2.代码实现
object Task1{
def main(args:Array[String]) : Unit = {
println("(1)")
//定义一个列表List1,内容为(“a”, “b” ,“c”, “d”, “e”, “f”, “g”)
var List1: List[String] = List("a", "b", "c", "d", "e", "f", "g")
println("Initialize List1: "+List1)
//定义列表List2,内容为(“m”, “n”, “a”, “b” , “e”)
val List2: List[String] = List("m", "n", "a", "b", "e")
println("Initialize List2: "+List2)
//使用concat方法在列表List1开头添加列表List2的元素
List1=List.concat(List2,List1)
println("Add List2 at the beginning of List1: "+List1)
println("(2)")
//使用distinct方法去除List1列表的重复元素
List1=List1.distinct
println("Remove duplicate elements in List1: "+List1)
//使用indexOf方法从指定位置 0 开始查找元素d第一次出现的位置,并输出结果
println("find the first occurrence of \"d\", starting at location 0: "+List1.indexOf("d",0))
println("(3)")
//使用reverse方法对List1进行列表反转
List1=List1.reverse
println("reverse List1: "+List1)
println("(4)")
//使用sorted方法对List1排序,再使用reverse方法得到list1降序排序
List1=List1.sorted.reverse
println("sort List1 down: "+List1)
}
}
3.测试结果
三、scala面向对象编程
- 实验内容
定义一个抽象类Shape,包含两个函数用以计算面积area()以及周长perimeter(),以此继承出计算长方形Rectangle、圆形Circle、梯形Trapezium的类。其中Rectangle类有2个成员变量:width与height;Circle类有一个成员变量radius;Trapezium有3个成员变量top,bottom,height。定义两个特质并在各个派生类中使用:MyMath定义加减乘除函数操作,PrintInfo用于输出shape的相关信息,例如:该长方形的周长为xx,面积为xx。
要求:实现4个类,2个特质,以及完成主函数内容(给定相关参数,分别计算3种形状的周长与面积)
2.代码实现
//定义MyMath特质,并定义加减乘除函数操作
//拥有MyMath特质的类可以使用MyMath中定义的加减乘除函数进行计算操作
trait MyMath{
def add(a:Double, b:Double):Double={
var res:Double = 0.0
res = a + b
return res
}
def sub(a:Double, b:Double):Double={
var res:Double = 0.0
res = a - b
return res
}
def mul(a:Double, b:Double):Double={
var res:Double = 0.0
res = a * b
return res
}
def div(a:Double, b:Double):Double={
var res:Double = 0.0
res = a / b
return res
}
}
//定义PrintInfo特质,用于输出shape的相关信息
trait PrintInfo{
def printinfo(){
println(this.toString)
}
}
//定义抽象类Shape,包含两个函数用以计算面积area()以及周长perimeter()
abstract class Shape(){
def area(): Double
def perimeter(): Double
}
//Rectangle类继承Shape抽象类,并拥有上述定义的两个特质
class Rectangle(w:Double, h:Double) extends Shape() with MyMath with PrintInfo{
//Rectangle类有2个成员变量:width与height
var width:Double =w
var height:Double =h
//重载area面积计算函数
override def area(): Double = {
var res:Double = 0.0
//调用MyMath中的加减乘除函数进行长方形面积的计算
res = mul(width,height)
return res
}
//重载perimeter周长计算函数
override def perimeter(): Double ={
var res:Double = 0.0
//调用MyMath中的加减乘除函数进行长方形周长的计算
res = mul(2.0,add(width,height))
return res
}
def printInfo(){
var toString="该长方形的周长为"+perimeter+",面积为"+area
println(toString)
}
}
//Circle类继承Shape抽象类,并拥有上述定义的两个特质
class Circle(r:Double) extends Shape() with MyMath with PrintInfo{
//Rectangle类有1个成员变量:radius
var radius:Double =r
//重载area面积计算函数
override def area(): Double = {
var res:Double = 0.0
//调用MyMath中的加减乘除函数进行圆形面积的计算
res = mul(3.14, mul(radius, radius))
return res
}
//重载perimeter周长计算函数
override def perimeter(): Double ={
var res:Double = 0.0
//调用MyMath中的加减乘除函数进行圆形周长的计算
res = mul(3.14, mul(2.0, radius))
return res
}
def printInfo(){
var toString="该圆形的周长为"+perimeter+",面积为"+area
println(toString)
}
}
//Trapezium类继承Shape抽象类,并拥有上述定义的两个特质
class Trapezium(t:Double, b:Double, h:Double) extends Shape() with MyMath with PrintInfo{
var top:Double =t
var bottom:Double =b
var height:Double =h
//重载area面积计算函数
override def area(): Double = {
var res:Double = 0.0
//调用MyMath中的加减乘除函数进行梯形面积的计算
res = div(mul(add(top ,bottom),height),2.0)
return res
}
//重载perimeter周长计算函数
override def perimeter(): Double ={
var res:Double = 0.0
//调用MyMath中的加减乘除函数进行梯形周长的计算
res = add(add(top,bottom),mul(2.0,math.sqrt(add(mul(height,height),mul(div(sub(bottom,top),2.0),div(sub(bottom,top),2.0))))))
return res
}
def printInfo(){
var toString="该梯形的周长为"+perimeter+",面积为"+area
println(toString)
}
}
object Task2{
def main(args: Array[String]) {
val r=new Rectangle(10,30)
r.printInfo;
val c=new Circle(10)
c.printInfo;
val t=new Trapezium(10,30,10)
t.printInfo;
}
}
3.测试结果
四、统计学生成绩
- 实验内容
要求:尽量用函数式编程
学生的成绩清单格式如下所示,第一行为表头,各字段意思分别为学号、性别、课程名1,课程名2等,后面每一行代表一个学生的信息,各字段之间用空白符隔开,数据与习题2.3相同。
给定任何一个如下格式的清单(不同清单里课程数量可能不一样),要求尽可能采用函数式编程,统计出以下数据:
- 各门课程的平均成绩,方差,及格率,以及80分以上的人数;
- 每个同学所有课程的平均成绩;
- 每门课程排名前5位,以及后5位的学生学号。
2.代码实现
import scala.io.Source
object Ex03 {
def main(array: Array[String])={
// 假设数据文件在当前目录下传入
val inputFile = Source.fromFile("data.txt")
//”\\s+“是字符串正则表达式,将每行按空白字符(包括空格/制表符)分开
// 由于可能涉及多次遍历,同 toList 将 Iterator 装为 List
// originalData 的类型为 List[Array[String]]
val originalData = inputFile.getLines.map(_.split("\\s+")).toList
//使用head方法获取第一行再使用drop方法获取第一行中的课程名
val courseName = originalData.head.drop(2)
// 去除第一行剩下的数据为学生信息数据
val students = originalData.tail
// 统计函数1,参数为需要常用统计的行
//用到了外部变量 courseNum,属于闭包函数
//作用是统计出各门课程的平均成绩,方差,及格率,以及80分以上的人数
def statistic1 (lines:List[Array[String]])={
// for 推导式,对每门课程生成一个四元组,分别表示平均成绩,方差,及格率,以及80分以上的人数
for (i<-2 to courseName.length+1)yield{
// 取出需要统计的列,将其中元素转化为Double
val temp = lines map{
elem=>elem(i).toDouble
}
// 新列表中的元素为temp中元素与平均值差的平方
val temp2 = temp map{
elem=>(elem-temp.sum/lines.length)*(elem-temp.sum/lines.length)
}
//平均值:使用sum方法对统计列求和,再除以列表长度lines.length
//方差:使用sum方法对temp2求和,再除以列表长度lines.length
//及格率:filter方法过滤出成绩>=60的元素,用length方法求出过滤后列表长度再除以原列表长度
//80分以上的人数:filter方法过滤出成绩>=80的元素,用length方法求出过滤后列表长度
(temp.sum/lines.length,temp2.sum/lines.length,temp.filter(s=>s>=60.0).length.toDouble/lines.length,temp.filter(s=>s>=80.0).length)
}
}
//统计函数2,统计每个同学所有课程的平均成绩
def statistic2 (lines:List[Array[String]])={
// for 推导式,对每个同学生成一个二元组,分别是学生的学号和平均成绩
for (i<-0 to lines.length-1)yield{
//学号:对每一行使用toList方法转成List后,用head方法得到第一个元素就是学生的学号
//平均成绩:对每一行使用drop(2)方法保留成绩信息,用toArray方法转成Array后将每个元素转成Double再求和
//而后除以课程数量
(students(i).toList.head,students(i).drop(2).toArray.map(s=>s.toDouble).sum/courseName.length.toDouble)
}
}
// 输出结果函数1,用于输出统计函数1的结果
def printResult1(theresult:Seq[(Double,Double,Double,Int)]): Unit ={
// 遍历前调用 zip 方法将课程名容器和结果容器合并,合并结果为二元组容器
(courseName zip theresult)foreach{
case (name,result)=>
println(f"${name+":"}%-10s${result._1}%5.2f${result._2}%12.2f${result._3}%9.2f${result._4}%8.0f")
}
}
//输出结果函数3,用于统计并输出每门课程排名前5位,以及后5位的学生学号。
def printResult3(): Unit ={
var num=2 //课程起始列
(courseName)foreach{
case (name)=>
println(f"\n${name+"_max(5):"}%-10s")
//输出每门课程排名前5位的学生学号
//将成绩信息转成Array后,使用sortBy方法根据课程所在列排序
//而后反转为降序排序,使用take方法取前5位同学的学号输出
students.toArray.sortBy(s=>s(num)).reverse.take(5).foreach(s=>print(s(0)+"\t"))
println(f"\n${name+"_min(5):"}%-10s")
//输出每门课程排名后5位的学生学号,方法同上
//区别是在使用sortBy方法根据课程所在列排序后不需要反转
students.toArray.sortBy(s=>s(num)).take(5).foreach(s=>print(s(0)+"\t"))
num = num+1
}
println()
}
println("(1)")
val allResult1 = statistic1(students)
println("course average variance pass >80")
printResult1(allResult1)
println("(2)")
val allResult2 = statistic2(students)
println(" Id average")
for (i<-0 to allResult2.length-1){
println(f"${allResult2(i)._1}%-10s${allResult2(i)._2}%5.2f")
}
print("(3)")
printResult3()
}
}
3.测试结果