《Scala编程》课程作业
第一题、百元喝酒
作业要求:每瓶啤酒2元,3个空酒瓶或者5个瓶盖可换1瓶啤酒。100元最多可喝多少瓶啤酒?(不允许借啤酒)
思路:利用递归算法,一次性买完,然后递归算出瓶盖和空瓶能换的啤酒数
/** * @author DOUBLEXI * @date 2021/8/17 15:33 * @description * * 作业要求:每瓶啤酒2元,3个空酒瓶或者5个瓶盖可换1瓶啤酒。100元最多可喝多少瓶啤酒? * (不允许借啤酒)思路:利用递归算法,一次性买完,然后递归算出瓶盖和空瓶能换的啤酒数 */ object HundredBeer { /** * @param sumBeers 啤酒总数 * @param bottle 剩余空瓶子数量 * @param cap 剩余啤酒盖数量 * @return 返回总啤酒总数 */ def beer(sumBeers: Int, bottle: Int, cap: Int): Int = { println(s"啤酒总数:$sumBeers,剩余空瓶子:$bottle,剩余啤酒盖:$cap") // 递归边界条件 if (bottle < 3 && cap < 5) return sumBeers var b1 = bottle / 3 // 现有空瓶子能够兑换的啤酒数 var b2 = bottle % 3 // 空瓶子兑换啤酒后,剩余的空瓶子 var c1 = cap / 5 // 现有啤酒盖能够兑换的啤酒数 var c2 = cap % 5 // 啤酒盖兑换啤酒后,剩余的空瓶子 /* 上面一顿兑换操作后,啤酒总数为:b1 + c1 + sumBeers 剩余的空瓶子总数为:b1 + c1 + b2 剩余的啤酒盖总数为:b1 + c1 + c2 继续递归兑换 */ beer(b1 + c1 + sumBeers, b1 + c1 + b2, b1 + c1 + c2) } def main(args: Array[String]): Unit = { var sumBeers: Int = 100 / 2 // 初始化啤酒总数 var bottle, cap : Int = sumBeers // 初始化空瓶子数=啤酒盖数=啤酒总数 val beers = beer(sumBeers, bottle, cap) // 递归求能够喝到的啤酒总数 println(s"100元最多可以喝$beers 瓶啤酒") } }
运行结果如下:
第二题、人机猜拳
1.1 作业需求
1. 选取对战角色
2. 开始对战,用户出拳,与对手进行比较,提示胜负信息
3. 猜拳结束算分,平局都加一分,获胜加二分,失败不加分
4 . 循环对战,当输入“n”时,终止对战,并显示对战结果
5. 游戏结束后显示得分
如下图所示:
1.2 作业分析
分析业务逻辑,抽象出类、类的属性和方法,如下:
1. 创建用户类User,定义类的属性(name,score)和类的方法(showFist())
2. 创建计算机类Computer,定义类的属性(name,score)和类的方法(showFist())
3. 实现计算机随机出拳
4. 创建游戏类Game,定义类的属性(甲方玩家、乙方玩家、对战次数)
5. 编写初始化方法、游戏开始方法
1、编写用户类User
/** * @author DOUBLEXI * @date 2021/8/17 16:45 * @description */ /** * 用户类User * @param name 用户名 * @param score 用户得分 */ class User(var name: String, var score: Int) { var draw: Int = 0 // 平局数 var defeat: Int = 0 // 败局数 var victory: Int = 0 // 胜局数 // 让用户自己出拳,输入1.剪刀 2.石头 3.布 def showFist(): Int = { var fist = scala.io.StdIn.readInt() while (fist != 1 && fist != 2 && fist != 3) { println("输入不合法,请重新输入!!!") fist = scala.io.StdIn.readInt() } return fist } }
2、编写电脑类Computer
/** * @author DOUBLEXI * @date 2021/8/17 17:09 * @description */ /** * 电脑类 * @param name 电脑名字 * @param score 电脑得分 */ class Computer(var name: String, var score: Int) { var draw: Int = 0 // 电脑平局数 var defeat: Int = 0 // 电脑败局数 var victory: Int = 0 // 电脑胜局数 /** * 随机出拳,1-3 * @return 返回一个1-3的数字 */ def showFist(): Int = { scala.util.Random.nextInt(3) + 1 } }
3、编写游戏类Game
/** * @author DOUBLEXI * @date 2021/8/17 17:11 * @description */ /** * 游戏类 */ class Game { var user: User = _ // 定义玩家 var computer: Computer = _ // 定义电脑 var battleNum: Int = _ // 定义对战次数 /** * 游戏初始化方法 */ def gameInit(): Unit = { val motd = """ ------------------欢迎进入游戏世界----------------- ************************************** **************猜拳开始***************** ************************************** """.stripMargin println(motd) // 打印游戏欢迎信息 println("请选择对战角色:(1.刘备 2.关羽 3.张飞)") // 用户选择电脑的角色 var roleNum = scala.io.StdIn.readInt() while (roleNum != 1 && roleNum != 2 && roleNum != 3) { println("没有这个选项,请重新输入!") roleNum = scala.io.StdIn.readInt() } val battleRole = roleNum match { case 1 => "刘备" case 2 => "关羽" case 3 => "张飞" } println(s"你选择了与${battleRole}对战") user = new User("游客", 0) // 初始化玩家信息 computer = new Computer(s"$battleRole", 0) // 初始化电脑信息 battleNum = 0 // 初始化对战信息 } /** * 游戏主程,游戏开始方法 * 循环对战,直到输入n终止 */ def startGame(): Unit = { var isExist = false var isStartNext: Char = 'y' // 循环对战 while (!isExist) { battleNum += 1; // 对战次数+1 // 玩家出拳 println("请出拳!1.剪刀 2.石头 3.布") var userFist = user.showFist() userFist match { case 1 => println("你出拳:剪刀") case 2 => println("你出拳:石头") case 3 => println("你出拳:布") } // 电脑出拳 println(s"${computer.name}出拳!") var computerFist = computer.showFist() computerFist match { case 1 => println(s"${computer.name}出拳:剪刀") case 2 => println(s"${computer.name}出拳:石头") case 3 => println(s"${computer.name}出拳:布") } // 调用judge方法,判断胜负 judge(userFist, computerFist) // 根据用户输入判断是否开启下一轮游戏,n退出 println("是否开始下一轮(y / n)") isStartNext = scala.io.StdIn.readChar() while (isStartNext != 'n' && isStartNext != 'N' && isStartNext != 'Y' && isStartNext != 'y') { println("您的输入不正确,请重新输入!") isStartNext = scala.io.StdIn.readChar() } if (isStartNext =='n'|| isStartNext == 'N') isExist = true } println("退出游戏!") // 打印得分成绩 echoResult } /** * 判断猜拳胜负 * @param userFist 用户出拳 * @param computerFist 电脑出拳 */ def judge(userFist: Int, computerFist: Int): Unit = { val judgeNum = userFist match { case 1 => if (computerFist == 1) 0 else if (computerFist == 2) -1 else 1 case 2 => if (computerFist == 1) 1 else if (computerFist == 2) 0 else -1 case 3 => if (computerFist == 1) -1 else if (computerFist == 2) 1 else 0 } if (judgeNum == 0) { println("结果:和局! 下次继续努力!") user.score += 1 user.draw += 1 computer.score += 1 computer.draw += 1 } else if (judgeNum == 1) { println("结果:恭喜,你赢啦!") user.score += 2 user.victory += 1 computer.defeat += 1 } else { println("结果:你输了,下次继续努力!") user.defeat += 1 computer.victory += 1 } } /** * 打印得分成绩 */ def echoResult(): Unit = { println("---------------------------------------------------------") println(s"${user.name} VS ${computer.name}") println(s"对战次数${battleNum}次") println() println() println("姓名\t等分\t胜局\t和局\t负局") println(s"${user.name}\t${user.score}\t\t${user.victory}\t\t${user.draw}\t\t${user.defeat}") println(s"${computer.name}\t${computer.score}\t\t${computer.victory}\t\t${computer.draw}\t\t${computer.defeat}") } } /** * 游戏主程入口 */ object Game { def main(args: Array[String]): Unit = { val game = new Game // 定义一个游戏对象 game.gameInit() // 游戏初始化 println("要开始么?y/n") var isStart = scala.io.StdIn.readChar() while (isStart != 'Y' && isStart != 'y' && isStart != 'n' && isStart != 'N') { println("没有这个选项,请重新输入!") isStart = scala.io.StdIn.readChar() } // 开始游戏 if (isStart == 'Y' || isStart == 'y') game.startGame } }
运行结果如下:
不合法输入:
打印得分:
第三题、用户位置时长统计
现有如下数据需要处理:
字段:用户ID,位置ID,开始时间,停留时长(分钟)
4行样例数据:
UserA,LocationA,8,60
UserA,LocationA,9,60
UserB,LocationB,10,60
UserB,LocationB,11,80
样例数据中的数据含义是:用户UserA,在LocationA位置,从8点开始,停留了60钟
处理要求:
1、对同一个用户,在同一个位置,连续的多条记录进行合并
2、合并原则:开始时间取最早时间,停留时长累计求和
答:算法如下:
1、定义一个样例类,用于存储每个用户的浏览数据
/** * 定义一个样例类:装载用户时长数据 * @param userName 用户名 * @param location 用户位置 * @param startTime 用户开始浏览时间 * @param duration 用户本次浏览时长 */ case class UserInfo(userName:String, location:String, startTime:Int, duration:Int)
2、在object主程序里,定义一个列表,用于存储各个用户的浏览信息。
// 定义一个列表,装载各个用户的时长数据 val userInfoList:List[UserInfo] = List( UserInfo("UserA", "LocationA", 8, 60), UserInfo("UserA", "LocationA", 9, 60), UserInfo("UserB", "LocationB", 10, 60), UserInfo("UserB", "LocationB", 11, 80) )
3、接下来就是要对该用户浏览信息的列表数据做处理。
这里使用scala的算子来作数据处理。
因为要对同一个用户、同一个位置的多条记录做合并,所以用到groupby算子,把这两个字段提出来作为一个key,然后把数据做聚合
// 因为要对同一个用户,同一个位置的数据做合并,所以把这两个字段提出来,一起作为key聚合 val userMap = userInfoList.groupBy(t=>t.userName + ',' + t.location) println(s"userMap: $userMap")
聚合后userMap数据为:
userMap: Map(UserB,LocationB -> List(UserInfo(UserB,LocationB,10,60), UserInfo(UserB,LocationB,11,80)), UserA,LocationA -> List(UserInfo(UserA,LocationA,8,60), UserInfo(UserA,LocationA,9,60)))
对聚合后的数据,按照startTime排序,方便后面取最早的时间
// 对聚合后的数据,按照startTime排序 val orderByUserMap = userMap.mapValues(t => t.sortBy(x=>x.startTime)) println(s"orderByUserMap: $orderByUserMap")
排完序后的数据为:
orderByUserMap: Map(UserB,LocationB -> List(UserInfo(UserB,LocationB,10,60), UserInfo(UserB,LocationB,11,80)), UserA,LocationA -> List(UserInfo(UserA,LocationA,8,60), UserInfo(UserA,LocationA,9,60)))
取得最早的时间,并统计浏览时长之和
var firstTime = 0 // totalMap: Map(UserB,LocationB -> 140, UserA,LocationA -> 120) val totalMap = orderByUserMap.mapValues( t => { // 将相同用户、相同位置下,最早的时间的作为firstTime firstTime = t.head.startTime // 时长字段相加,并作为totalMap的value返回 var sum = t.map(x=> x.duration).sum sum }) println(s"totalMap: $totalMap")
统计完的结果map如下:
totalMap: Map(UserB,LocationB -> 140, UserA,LocationA -> 120)
最后,再重新组合,打印最终结果数据:
// 重新组合数据,打印最终结果 /** * datas就是totalMap的key,也就是(userName,location) * sumTIme就是totalMap的value,也就是上面统计的sum */ totalMap.foreach{ case (datas, sumTime) => println(s"$datas,$firstTime,$sumTime") }
最终结果如下:
UserB,LocationB,10,140 UserA,LocationA,8,120
合并总的程序如下:
/** * @author DOUBLEXI * @date 2021/8/18 15:16 * @description */ /** * 定义一个样例类:装载用户时长数据 * @param userName 用户名 * @param location 用户位置 * @param startTime 用户开始浏览时间 * @param duration 用户本次浏览时长 */ case class UserInfo(userName:String, location:String, startTime:Int, duration:Int) object UserLocationDurationCount { def main(args: Array[String]): Unit = { // 定义一个列表,装载各个用户的时长数据 val userInfoList:List[UserInfo] = List( UserInfo("UserA", "LocationA", 8, 60), UserInfo("UserA", "LocationA", 9, 60), UserInfo("UserB", "LocationB", 10, 60), UserInfo("UserB", "LocationB", 11, 80) ) // 因为要对同一个用户,同一个位置的数据做合并,所以把这两个字段提出来,一起作为key聚合 val userMap = userInfoList.groupBy(t=>t.userName + ',' + t.location) println(s"userMap: $userMap") // 对聚合后的数据,按照startTime排序 val orderByUserMap = userMap.mapValues(t => t.sortBy(x=>x.startTime)) println(s"orderByUserMap: $orderByUserMap") var firstTime = 0 // totalMap: Map(UserB,LocationB -> 140, UserA,LocationA -> 120) val totalMap = orderByUserMap.mapValues( t => { // 将相同用户、相同位置下,最早的时间的作为firstTime firstTime = t.head.startTime // 时长字段相加,并作为totalMap的value返回 var sum = t.map(x=> x.duration).sum sum }) println(s"totalMap: $totalMap") // 重新组合数据,打印最终结果 /** * datas就是totalMap的key,也就是(userName,location) * sumTIme就是totalMap的value,也就是上面统计的sum */ totalMap.foreach{ case (datas, sumTime) => println(s"$datas,$firstTime,$sumTime") } } }
运行结果如下: