问题
I have functions which allow the user to compare two teams contained within a map. The data contained in the map is read in from a text file which contains information about football teams and their points tallies for the past 5 seasons. The data is stored as Map[String, List[Int]]:
Manchester United, 72, 86, 83, 90, 94
Manchester City, 80, 84, 91, 77, 88
Chelsea, 76, 85, 92, 87, 84
Arsenal, 70, 79, 81, 76, 83
Liverpool, 69, 77, 80, 73, 79
The functions below allow the user to enter the names of two teams and compare the difference between the most recent (last) points tallies for the two teams.
val teamdata = readTextFile("teams.txt")
//User presses 2 on keyboard, this invokes menuCompareTeams which invokes compareTeams
def menuOptionTwo(): Boolean = {
//2 - compare 2 teams selected by the user
menuCompareTeams(compareTeams)
true
}
//Function which displays the results of compareTeams
def menuCompareTeams(f: (String, String) => ((String, Int), (String, Int), String)) = {
val input = f(readLine("Enter first team to compare>"),
readLine("Enter second team to compare>"))
println(s"""|Team 1: ${input._1._1} - Points: ${input._1._2}
|Team 2: ${input._2._1} - Points: ${input._2._2}
|${input._3}""".stripMargin)
}
///Function which compares the 2 teams - invoked by menuCompareTeams
def compareTeams(team1: String, team2: String): ((String, Int), (String, Int), String) = {
def lastPoints(list: List[Int]): Int = list match {
case Nil => throw new Exception("Empty list")
case h :: Nil => h
case _ :: tail => lastPoints(tail)
}
val team1Points = teamdata.get(team1) match{
case Some(p) => lastPoints(p)
case None => 0
}
val team2Points = teamdata.get(team2) match{
case Some(p) => lastPoints(p)
case None => 0
}
val pointsComparison = if(team1Points > team2Points){
"The team who finished higher is: " + team1 + ", their total points tally for last season was: " + team1Points + ". There was a difference of " + (team1Points-team2Points) + " points between the two teams."
}
else if(team1Points == team2Points){
"The teams had equal points last season: " + (team1Points|team2Points)
}
else{
"The team who finished higher is: " + team2 + ", their total points tally for last season was: " + team2Points + ". There was a difference of " + (team2Points-team1Points) + " points between the two teams."
}
((team1, team1Points), (team2, team2Points), pointsComparison)
}
E.g. The correct output for when the user enters 'Manchester United' and 'Manchester City' is shown below:
Team 1: Manchester United - Points: 94
Team 2: Manchester City - Points: 88
The team who finished higher is: Manchester United, their total points tally for last season was: 94. There was a difference of 6 points between the two teams.
Is there a better or functional way to do what I am currently doing for the comparison of 2 teams?
EDIT: I have edited the question based on Alex's suggestion.
回答1:
"More Functional"
The one "side effect" you do have is throwing an exception for empty lists. If that is truly an exceptional case then lastPoints
should return a Try[Int]. Other than that you maintain referential transparency, use pattern matching, and use recursive functions so you can't get "more functional".
"Better"
You could use List#lastOption in lastPoints
(assuming you don't throw exceptions) instead of writing your own:
val lastPoints = (_ : List[Int]).lastOption
Which could then be used as follows:
val getPoints =
(team : String) => teamdata.get(team)
.flatMap(lastPoints)
.getOrElse(0)
val team1Points = getPoints(team1)
val team2Points = getPoints(team2)
In general, I always go searching for a collection method that solves my problem before trying to "roll my own". By relying on lastOption
, flatMap
, and getOrElse
there are fewer places for bugs to lurk.
来源:https://stackoverflow.com/questions/41492630/is-there-a-better-or-functional-way-to-compare-2-values-in-a-map-in-scala