如何基于Haskell中的分数列表创建排名?

时间:2018-07-28 15:31:21

标签: haskell

因此,我得到了一个得分列表,并希望从中创建一个排名列表。如果分数相同,他们将分享一个排名。

例如,如果我有一个得分列表,例如

[100, 100, 50, 50, 20]

生成的列表将是

[(100, 1), (100, 1), (50, 2), (50, 2), (20, 3)]

我想这是一个相当简单的任务,但是我还没有解决它。我试图通过模式匹配或折叠来做到这一点,但是没有任何运气。

我上次失败的方法如下:

scores = [100, 100, 50, 50, 20, 10]

ranks = foldr (\x acc -> if x == (fst $ last acc)
  then last acc:acc
  else (x, (+1) $ snd $ last acc):acc) [(head scores, 1)] scores

感谢您的帮助。

3 个答案:

答案 0 :(得分:5)

此解决方案与Willem的解决方案非常相似,不同之处在于它未明确使用递归。许多样式指南,包括the Haskell wiki,建议如果存在涉及高阶函数的简单实现,则避免显式递归。在您的情况下,您的函数是scanl的非常简单的用法,它使用一个累加值折叠一个列表(在您的情况下,累加器是当前的排名和得分),并存储中间结果。

ranks :: Eq a => [a] -> [(a, Int)]
-- Handle the empty case trivially.
ranks [] = []
-- Scan left-to-right. The first element of the result should always
-- have rank 1, hence the `(x, 1)' for the starting conditions.
ranks (x:xs) = scanl go (x, 1) xs
    -- The actual recursion is handled by `scanl'. `go' just
    -- handles each specific iteration.
    where go (curr, rank) y
              -- If the "current" score equals the next element,
              -- don't change the rank.
              | curr == y = (curr, rank)
              -- If they're not equal, increment the rank and
              -- move on.
              | otherwise = (y, rank + 1)

通过避免显式递归,可以一目了然地看到函数的作用。我可以看一下,立即看到scanl,并且知道该函数将以某种状态(等级)从左到右遍历列表,并产生中间结果。

答案 1 :(得分:3)

我们可以编写一个保持状态的递归算法:它正在分配的当前等级。每次算法都看两个元素。如果下一个元素相同,则排名递增,否则递增。

因此,我们可以像这样实现它:

rank :: Eq a => [a] -> [(a, Int)]
rank = go 1
    where go i (x:xr@(x2:_)) = (x, i) : go'
              where go' | x == x2 = go i xr
                        | otherwise = go (i+1) xr
          go i [x] = [(x, i)]
          go _ [] = []

因此,我们指定rank = go 1,从而通过1初始化”状态。每次我们使用go检查列表是否至少包含两个元素。在这种情况下,我们首先发出状态为(x, i)的第一个元素,然后对其余xr执行递归。根据第一个元素x是否等于第二个元素x2,我们增加或不增加状态。如果列表仅包含一个元素x,则我们返回[(x, i)];如果列表根本不包含任何元素,则返回空列表。

请注意,这是假设分数已经按照降序排列(或者是从“最佳”到“最差”的顺序,因为在某些游戏中“ 得分”有时是负面的) 。但是,如果不是这种情况,我们可以使用sort步骤作为预处理。

答案 2 :(得分:2)

这是一个简单的单行代码,将一些现成的文件与列表理解一起放置。

import Data.List
import Data.Ord (Down (..))

rank :: Ord a => [a] -> [(a, Int)]
rank xs = [(a, i) | (i, as) <- zip [1..] . group . sortBy (comparing Down) $ xs
          , a <- as]

如果列表已经按照相反的顺序排序,则可以省略sortBy (comparing Down)