F1与Haskell结果 [英] F1 Results with Haskell

查看:155
本文介绍了F1与Haskell结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最简单的解决方案是什么?

定义函数f1Results中用于计算公式1比赛系列的函数。参数是一个列表,其元素是基于每个大奖赛结果的列表。每个这样的列表应包含由GP结果列出的竞争者的名字(冠军是列表中的第一名)。该功能的结果必须是按照整个F1赛季中的点数排序的F1飞行员姓名列表。

  f1Results :: [[String]]  - > [字符串] 

必须为每个位置授予积分(例如:第一名25分,第二名18分等)。

  pointsScoring :: [ Int] 
pointsScoring = [25,18,15,12,10,8,6,4,2,1]


解决方案

你有一个不变的评分是什么 $ b

 得分:: [Int] 
得分= [25,18,15,12,10,8,6,4,2,1]

然后,您需要一种方法将驾驶员与他们得分进行配对。无论何时在Haskell中配对两件事,规范的选择都是使用一个元组。从两个列表构造元组列表的最简单方法是 zip 函数:

  zip :: [a]  - > [b]  - > [(a,b)] 

在这种情况下,可以用来为比赛分配分数:

  assignScores :: [String]  - > [(String,Int)] 
assignScores race = zip比赛得分

现在,我们需要一种方法来为每场比赛的车手总分。我们希望能够变成像

  [(Bob,12),(Joe,10), (Bob,18),(Joe,25)] 


$ b $

  [(Bob,30),(Joe,35)] 
pre>

最简单的方法是制作所有比赛所有比分的单一列表

  assignAllScores :: [[String]]  - > [(String,Int)] 
assignAllScores races = concatMap assignScores races

然后我们可以使用 sortBy 来自 Data.List 以获取所有相同名称相邻的名称

  sortBy ::(a  - > a  - >排序) - > [a]  - > [a] 
比较:: Ord a => a - > a - >订购

sortByDriver :: [(String,Int)] - > [(String,Int)]
sortByDriver races = sortBy(\(n1,s1)(n2,s2) - >比较n1 n2)比赛
groupBy (也来自 Data.List c>)将其全部按名称分组。 [a] - > [[a]]

groupByDriver :: [(String,Int)] - > [[(String,Int)]]
groupByDriver races = groupBy(\(n1,s1)(n2,s2) - > n1 == n2)races

但是这给了我们一个像

  [ [(Bob,12),(Bob,18)],[(Joe,10),(Joe,25)]] 

我们现在需要一种方法将其转换为表单。

  [(Bob,[12,18]),(Joe,[10,25])] 

其中所有分数汇总回列表中,我们根本不重复名称。这是留作练习。

  aggregateScores :: [[(String,Int)]]  - > [(String,[Int])] 

然后我们可以最终计算这些分数的总和



  sumScores :: [(String,[Int])]  - > [(String,Int)] 
sumscores races = map(\(name,scores) - >(name,sum scores))races

然后最后我们可以按照分数排序,以便让每个人都能按顺序排列。

  sortByScore :: [(String,Int)]  - > [(String,Int)] 
sortByScore races = sortBy(\(n1,s1)(n2,s2) - > compare s2 s1)races

请注意,我有比较s2 s1 而不是比较s1 s2 ,这意味着它将按降序排序而不是升序排列。



最后一步是去掉分数,现在我们有我们的驱动程序列表从赢家到输家的顺序

  removeScores :: [(String,Int)]  - > [String] 
removeScores races = map fst races

  f1Results :: [[String]]  - > [String] 
f1Results races =
removeScores $
sortByScore $
sumScores $
aggregateScores $
groupByDriver $
sortByDriver $
assignAllScores比赛






有几个技巧可以做到这一点代码较短,即 Data.Function.on Data.Ord.comparing ,以及 Control.Arrow 。不要把它当做家庭作业,我只是想展示一个代码少的代码。

  import Data.List 
导入Data.Function(on)
导入Data.Ord(比较)

得分:: [Int]
得分= [25,18,15,12, 10,8,6,4,2,1]

f1Results :: [[String]] - > [String]
f1Results =
map fst。 sortBy(on(flip compare)snd)。
map((head *** sum)。unzip)。
groupBy(on(==)fst)。 sortBy(比较fst)。
concatMap(`zip` scoring)

或者使用 Data。 Map

  import Data.Map(assocs,fromListWith)
import Data.List
导入Data.Ord(比较)

得分:: [Int]
得分= [25,18,15,12,10,8,6,4,2, 1]

f1Results :: [[String]] - > [String]
f1Results =
反向。地图fst。 sortBy(比较snd)。
assocs。 fromListWith(+)。
concatMap(`zip`评分)


What is the most simple solution to do this ?

Define a function for calculating the series of Formula 1 race in function "f1Results". The parameter is a list whose elements are lists based on the result of each Grand Prix. Each such list shall contain the names of the competitors listed by the GP result (the winner is the first of the list). The result of the function has to be a list of names of F1 pilots sorted by number of points in the overall F1 season.

   f1Results :: [[String]] -> [String]

Points have to be granted for each position like in the list bellow (again the 25 is for the winner) pointsScoring (eg: 1st place 25 points, 2nd place 18 points, etc.).

   pointsScoring :: [Int]
   pointsScoring = [25, 18, 15, 12, 10, 8, 6, 4, 2, 1]

解决方案

You have a constant for what the scoring is

scoring :: [Int]
scoring = [25, 18, 15, 12, 10, 8, 6, 4, 2, 1]

Then you need a way for pairing up a driver with the score they got. Whenever you're pairing two things in Haskell, the canonical choice is to use a tuple. The easiest way to construct a list of tuples from two lists is the zip function:

zip :: [a] -> [b] -> [(a, b)]

And in this case can be used to assign scores for a race:

assignScores :: [String] -> [(String, Int)]
assignScores race = zip race scoring

Now, we need a way to total up the scores for a driver for each race. We want to be able to turn something like

[("Bob", 12), ("Joe", 10), ("Bob", 18), ("Joe", 25)]

into

[("Bob", 30), ("Joe", 35)]

The easiest way would be to make a single list of all the scores for all the races

assignAllScores :: [[String]] -> [(String, Int)]
assignAllScores races = concatMap assignScores races

Then we can use sortBy from Data.List to get all the same names next to each other

sortBy :: (a -> a -> Ordering) -> [a] -> [a]
compare :: Ord a => a -> a -> Ordering

sortByDriver :: [(String, Int)] -> [(String, Int)]
sortByDriver races = sortBy (\(n1, s1) (n2, s2) -> compare n1 n2) races

Then we can use groupBy (also from Data.List) to group them all by name

groupBy :: (a -> a -> Bool) -> [a] -> [[a]]

groupByDriver :: [(String, Int)] -> [[(String, Int)]]
groupByDriver races = groupBy (\(n1, s1) (n2, s2) -> n1 == n2) races

But this gives us a list like

[[("Bob", 12), ("Bob", 18)], [("Joe", 10), ("Joe", 25)]]

We now need a way to convert this into the form

[("Bob", [12, 18]), ("Joe", [10, 25])]

where all the scores are aggregated back into a list, and we don't repeat the names at all. This is left as an exercise.

aggregateScores :: [[(String, Int)]] -> [(String, [Int])]

Then we can finally calculate the sum of these scores

sumScores :: [(String, [Int])] -> [(String, Int)]
sumScores races = map (\(name, scores) -> (name, sum scores)) races

Then finally we can sort by the scores to get everyone in order

sortByScore :: [(String, Int)] -> [(String, Int)]
sortByScore races = sortBy (\(n1, s1) (n2, s2) -> compare s2 s1) races

Notice that I have compare s2 s1 instead of compare s1 s2, this means it will be sorted in descending order instead of ascending order.

The last step is to strip out the scores, now we have our list of drivers in order from winner to loser

removeScores :: [(String, Int)] -> [String]
removeScores races = map fst races

So to combine everything together into one function

f1Results :: [[String]] -> [String]
f1Results races =
    removeScores $
    sortByScore  $
    sumScores    $
    aggregateScores $
    groupByDriver $
    sortByDriver $
    assignAllScores races


There are several tricks that can make this code shorter, namely Data.Function.on, Data.Ord.comparing, and a fun operator from Control.Arrow. Don't turn this in as homework, I just wanted to show an alternative that uses less code.

import Data.List
import Data.Function (on)
import Data.Ord (comparing)

scoring :: [Int]
scoring = [25, 18, 15, 12, 10, 8, 6, 4, 2, 1]

f1Results :: [[String]] -> [String]
f1Results =
    map fst . sortBy (on (flip compare) snd) .
    map ((head *** sum) . unzip) .
    groupBy (on (==) fst) . sortBy (comparing fst) .
    concatMap (`zip` scoring)

Or using Data.Map:

import Data.Map (assocs, fromListWith)
import Data.List
import Data.Ord (comparing)

scoring :: [Int]
scoring = [25, 18, 15, 12, 10, 8, 6, 4, 2, 1]

f1Results :: [[String]] -> [String]
f1Results =
    reverse . map fst . sortBy (comparing snd) .
    assocs . fromListWith (+) .
    concatMap (`zip` scoring)

这篇关于F1与Haskell结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆