计算Haskell中排序列表的最频繁出现次数

时间:2011-10-04 08:33:54

标签: haskell

问题是计算排序的整数列表的模式(最常出现的值)。

[1,1,1,1,2,2,3,3]  -> 1
[2,2,3,3,3,3,4,4,8,8,8,8] -> 3 or 8
[3,3,3,3,4,4,5,5,6,6] -> 3

只需使用Prelude库。

Prelude库中的函数filter,map,foldr?

4 个答案:

答案 0 :(得分:5)

从头开始。

您想要通过序列并获得整数的最大频率。

这听起来像是折叠的工作,因为折叠在给出最终结果之前会经历一个聚合值的序列。

foldl :: (a -> b -> a) -> a -> [b] -> a

foldl的类型如上所示。我们已经可以填写一些(我发现这可以帮助我找出我需要的类型)

foldl :: (a -> Int -> a) -> a -> [Int] -> a

我们需要通过折叠来获得价值。我们必须跟踪当前运行和当前计数

data BestRun = BestRun {
   currentNum :: Int,
   occurrences :: Int,
   bestNum :: Int,
   bestOccurrences :: Int
}

所以现在我们可以填写更多内容:

foldl :: (BestRun -> Int -> BestRun) -> BestRun -> [Int] -> BestRun

所以我们想要一个执行聚合的函数

f :: BestRun -> Int -> BestRun
f (BestRun current occ best bestOcc) x
  | x == current = (BestRun current (occ + 1) best bestOcc) -- continuing current sequence
  | occ > bestOcc = (BestRun x 1 current occ) -- a new best sequence
  | otherwise      = (BestRun x 1 best bestOcc) -- new sequence

现在我们可以使用foldl作为

来编写函数
bestRun :: [Int] -> Int
bestRun xs = bestNum (foldl f (BestRun 0 0 0 0) xs)

答案 1 :(得分:5)

  

Prelude库中的函数filter,map,foldr?

停止...... Hoogle时间!

你知道吗,Hoogle告诉你一个函数来自哪个模块? Hoolging map会在搜索页面上生成此信息:

  

map ::(a - > b) - > [a] - >并[b]

     

base Prelude,base Data.List

这意味着地图已在PreludeData.List中定义。您可以勾选其他功能,同样看到它们确实在Prelude中。

您还可以查看Haskell 2010 > Standard Preludethe Prelude hackage docs

因此我们被允许mapfilterfoldr以及Prelude中的其他任何内容。非常好。让我们从Landei的想法开始,将列表转换为列表列表。

groupSorted :: [a] -> [[a]]
groupSorted = undefined
-- groupSorted [1,1,2,2,3,3] ==> [[1,1],[2,2],[3,3]]

我们应该如何实施groupSorted?好吧,我不知道。我们稍后再考虑一下。假装我们已经实现了它。我们如何使用它来获得正确的解决方案?我假设可以选择一个正确的解决方案,如果有多个(如第二个例子)。

mode :: [a] -> a
mode xs = doSomething (groupSorted xs)
  where doSomething :: [[a]] -> a
        doSomething = undefined
        -- doSomething [[1],[2],[3,3]] ==> 3
-- mode [1,2,3,3] ==> 3

我们在列表中使用groupSorted之后需要做一些事情。但是什么?嗯......我们应该在列表列表中找到最长的列表。对?这将告诉我们哪个元素在原始列表中出现最多。然后,一旦我们找到最长的子列表,我们想要返回其中的元素。

chooseLongest :: [[a]] -> a
chooseLongest xs = head $ chooseBy (\ys -> length ys) xs
  where chooseBy :: ([a] -> b) -> [[a]] -> a
        chooseBy f zs = undefined
        -- chooseBy length [[1],[2],[3,3]] ==> [3,3]
-- chooseLongest [[1],[2],[3,3]] ==> 3

chooseLongest是之前的doSomething。我们的想法是,我们要在列表xs列表中选择最佳列表,然后选择其中一个元素(其head就可以了)。我通过创建一个更通用的函数chooseBy来定义它,它使用一个函数(在这种情况下,我们使用length函数)来确定哪个选项是最好的。

现在我们处于“艰难”的角色。倍。 chooseBygroupSorted都是折叠。我将引导您完成groupSorted,并将chooseBy留给您。

如何编写自己的折叠

我们知道groupSorted是一个折叠,因为它会消耗整个列表,并产生一些全新的东西。

groupSorted :: [Int] -> [[Int]]
groupSorted xs = foldr step start xs
  where step :: Int -> [[Int]] -> [[Int]]
        step = undefined
        start :: [[Int]]
        start = undefined

我们需要选择初始值start和步进函数step。我们知道他们的类型,因为foldr的类型是(a -> b -> b) -> b -> [a] -> b,在这种情况下,aInt(因为xs[Int],与[a])对齐,而我们想要最终得到的b[[Int]]

现在请记住,步进函数将逐个检查列表的元素,并使用step将它们融合到累加器中。我将调用当前检查的元素v和累加器acc

step v acc = undefined

请记住,从理论上讲,foldr从右到左都是有效的。所以假设我们有列表[1,2,3,3]。让我们逐步完成算法,从最右边的3开始,然后继续前进。

step 3 start = [[3]]

无论start是什么,当我们将其与3结合使用时,它应该最终为[[3]]。我们知道这一点,因为如果groupSorted的原始输入列表只是[3],那么我们希望[[3]]作为结果。但是,它不只是[3]。我们现在假装它只是[3,3][[3]]是新的累加器,我们想要的结果是[[3,3]]

step 3 [[3]] = [[3,3]]

我们应该如何处理这些投入?好吧,我们应该将3添加到内部列表中。但下一步呢?

step 2 [[3,3]] = [[2],[3,3]]

在这种情况下,我们应该创建一个包含2的新列表。

step 1 [[2],[3,3]] = [[1],[2],[3,3]]

就像上次一样,在这种情况下,我们应该在其中创建一个包含1的新列表。

此时我们遍历了整个输入列表,并获得了最终结果。那么我们如何定义step?似乎有两种情况,具体取决于vacc之间的比较。

step v acc@((x:xs):xss) | v == x    = (v:x:xs) : xss
                        | otherwise = [v] : acc

在一种情况下,vacc中第一个子列表的头部相同。在这种情况下,我们将v添加到同一个子列表中。但如果情况并非如此,那么我们将v放在自己的列表中,并将其添加到acc。那么start应该是什么?嗯,需要特殊待遇;我们只需使用[]并为其添加特殊模式匹配。

step elem [] = [[elem]]
start = []

你有它。你只需要确定startstep是什么,就可以了解你所做的一切。通过一些清理和eta减少:

groupSorted = foldr step []
  where step v [] = [[v]]
        step v acc@((x:xs):xss)
          | v == x    = (v:x:xs) : xss
          | otherwise = [v] : acc

这可能不是最有效的解决方案,但它有效,如果您以后需要优化,至少可以了解此功能的工作原理。

答案 2 :(得分:2)

我不想破坏所有的乐趣,但group函数会有所帮助。不幸的是,它是在Data.List中定义的,所以你需要自己编写。一种可能的方式是:

-- corrected version, see comments
grp [] = []
grp (x:xs) = let a = takeWhile (==x) xs
                 b = dropWhile (==x) xs
             in (x : a) : grp b

E.g。 grp [1,1,2,2,3,3,3]给出[[1,1],[2,2],[3,3,3]]。我想从那里你可以自己找到解决方案。

答案 3 :(得分:0)

我会尝试以下方法:

mostFrequent = snd . foldl1 max . map mark . group
   where
      mark (a:as) = (1 + length as, a)
      mark [] = error "cannot happen"   --  because made by group

请注意,它适用于包含可排序元素的任何有限列表,而不仅仅是整数。