Haskell中按车手统计积分并排序生成[String]列表求助
解决Haskell车手积分统计排序问题
核心思路
要完成这个任务,我们可以分四步处理:
- 展平嵌套的比赛数据列表
- 统计每个车手的总积分
- 按积分从高到低排序
- 提取排序后的车手姓名列表
推荐实现(使用Data.Map高效统计)
首先导入必要的模块:
import Data.Map (fromListWith, toList) import Data.List (sortBy) import Data.Ord (Down(..))
实现核心函数:
rankDrivers :: [[(String, Int)]] -> [String] rankDrivers races = let -- 1. 展平所有比赛的积分记录,把[[(String,Int)]]转为[(String,Int)] allResults = concat races -- 2. 用fromListWith累加相同车手的积分,得到车手-总积分的Map totalPointsMap = fromListWith (+) allResults -- 3. 将Map转为列表,并按积分降序排序(积分相同则保持原有顺序,可按需添加名字排序逻辑) sortedPairs = sortBy (\(_, p1) (_, p2) -> compare (Down p1) (Down p2)) (toList totalPointsMap) -- 4. 提取排序后的车手姓名 driverNames = map fst sortedPairs in driverNames
代码解释
concat races:把多场比赛的积分列表合并成一个扁平列表,方便统一处理。比如[[("A",10),("B",5)], [("A",8)]]会变成[("A",10),("B",5),("A",8)]。fromListWith (+) allResults:fromListWith遍历所有积分记录,遇到相同车手姓名时,用+累加积分,最终得到存储车手总积分的Map String Int。sortBy ...:将Map转换为[(String, Int)]列表后,用sortBy自定义排序规则。Down p让compare按积分降序排列,确保积分高的车手排在前面。map fst sortedPairs:从排序后的(姓名, 积分)对中提取姓名,得到最终的[String]列表。
手动实现(不用Map,适合理解底层逻辑)
如果想手动实现积分累加(帮助新手理解原理),可以用foldl'遍历构建积分列表:
import Data.List (sortBy, foldl') import Data.Ord (Down(..)) rankDrivers :: [[(String, Int)]] -> [String] rankDrivers races = let allResults = concat races -- 手动累加积分:遍历每条记录,更新现有车手积分或添加新车手 accumulate :: [(String, Int)] -> (String, Int) -> [(String, Int)] accumulate [] (name, pts) = [(name, pts)] accumulate ((n, p):rest) (name, pts) | n == name = (n, p + pts) : rest | otherwise = (n, p) : accumulate rest (name, pts) totalPoints = foldl' accumulate [] allResults sortedPairs = sortBy (\(_, p1) (_, p2) -> compare (Down p1) (Down p2)) totalPoints in map fst sortedPairs
测试示例
用以下测试数据验证:
sampleRaces :: [[(String, Int)]] sampleRaces = [ [("Lewis", 25), ("Max", 18), ("Charles", 15)], [("Max", 25), ("Lewis", 18), ("Carlos", 15)], [("Lewis", 25), ("Charles", 18), ("Max", 15)] ]
调用rankDrivers sampleRaces会得到:["Lewis", "Max", "Charles", "Carlos"],对应总积分68、58、33、15。
内容的提问来源于stack exchange,提问作者Bujak
相关产品推荐
相关产品推荐

