计算 Haskell 中排序列表中最常出现的数字

发布于 2024-12-08 08:05:57 字数 220 浏览 0 评论 0原文

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

[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 函数吗?

The question is to compute the mode (the value that occurs most frequently) of a sorted list of integers.

[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

Just use the Prelude library.

Are the functions filter, map, foldr in Prelude library?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

玻璃人 2024-12-15 08:05:57

从头开始。

您想要遍历一个序列并获得整数的最大频率。

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

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)

Starting from the beginning.

You want to make a pass through a sequence and get the maximum frequency of an integer.

This sounds like a job for fold, as fold goes through a sequence aggregating a value along the way before giving you a final result.

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

The type of foldl is shown above. We can fill in some of that already (I find that helps me work out what types I need)

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

We need to fold something through that to get the value. We have to keep track of the current run and the current count

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

So now we can fill in a bit more:

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

So we want a function that does the aggregation

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

So now we can write the function using foldl as

bestRun :: [Int] -> Int
bestRun xs = bestNum (foldl f (BestRun 0 0 0 0) xs)
最笨的告白 2024-12-15 08:05:57

Prelude库中有filter、map、foldr函数吗?

停止...胡格尔时间!

您知道 Hoogle 会告诉您函数来自哪个模块吗? Hoolging 地图 会在搜索页面上显示以下信息:

地图::(a -> b) -> [一]-> [b]

基础 Prelude,基础 Data.List

这意味着地图同时在 PreludeData.List 中定义。你可以搜索其他功能,同样可以看到它们确实在 Prelude 中。

您还可以查看 Haskell 2010 >标准 PreludePrelude hackage 文档。

因此我们可以进行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-> [一]-> 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

这可能不是最有效的解决方案,但它确实有效,如果您以后需要优化,您至少知道这个函数是如何工作的。

Are the functions filter, map, foldr in Prelude library?

Stop...Hoogle time!

Did you know Hoogle tells you which module a function is from? Hoolging map results in this information on the search page:

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

base Prelude, base Data.List

This means map is defined both in Prelude and in Data.List. You can hoogle the other functions and likewise see that they are indeed in Prelude.

You can also look at Haskell 2010 > Standard Prelude or the Prelude hackage docs.

So we are allowed to map, filter, and foldr, as well as anything else in Prelude. That's good. Let's start with Landei's idea, to turn the list into a list of lists.

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

How are we supposed to implement groupSorted? Well, I dunno. Let's think about that later. Pretend that we've implemented it. How would we use it to get the correct solution? I'm assuming it is OK to choose just one correct solution, in the event that there is more than one (as in your second example).

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

We need to do something after we use groupSorted on the list. But what? Well...we should find the longest list in the list of lists. Right? That would tell us which element appears the most in the original list. Then, once we find the longest sublist, we want to return the element inside it.

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 is the doSomething from before. The idea is that we want to choose the best list in the list of lists xs, and then take one of its elements (its head does just fine). I defined this by creating a more general function, chooseBy, which uses a function (in this case, we use the length function) to determine which choice is best.

Now we're at the "hard" part. Folds. chooseBy and groupSorted are both folds. I'll step you through groupSorted, and leave chooseBy up to you.

How to write your own folds

We know groupSorted is a fold, because it consumes the entire list, and produces something entirely new.

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

We need to choose an initial value, start, and a stepping function step. We know their types because the type of foldr is (a -> b -> b) -> b -> [a] -> b, and in this case, a is Int (because xs is [Int], which lines up with [a]), and the b we want to end up with is [[Int]].

Now remember, the stepping function will inspect the elements of the list, one by one, and use step to fuse them into an accumulator. I will call the currently inspected element v, and the accumulator acc.

step v acc = undefined

Remember, in theory, foldr works its way from right to left. So suppose we have the list [1,2,3,3]. Let's step through the algorithm, starting with the rightmost 3 and working our way left.

step 3 start = [[3]]

Whatever start is, when we combine it with 3 it should end up as [[3]]. We know this because if the original input list to groupSorted were simply [3], then we would want [[3]] as a result. However, it isn't just [3]. Let's pretend now that it's just [3,3]. [[3]] is the new accumulator, and the result we would want is [[3,3]].

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

What should we do with these inputs? Well, we should tack the 3 onto that inner list. But what about the next step?

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

In this case, we should create a new list with 2 in it.

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

Just like last time, in this case we should create a new list with 1 inside of it.

At this point we have traversed the entire input list, and have our final result. So how do we define step? There appear to be two cases, depending on a comparison between v and acc.

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

In one case, v is the same as the head of the first sublist in acc. In that case we prepend v to that same sublist. But if such is not the case, then we put v in its own list and prepend that to acc. So what should start be? Well, it needs special treatment; let's just use [] and add a special pattern match for it.

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

And there you have it. All you have to do to write your on fold is determine what start and step are, and you're done. With some cleanup and eta reduction:

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

This may not be the most efficient solution, but it works, and if you later need to optimize, you at least have an idea of how this function works.

戏剧牡丹亭 2024-12-15 08:05:57

我不想破坏所有的乐趣,但是 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

例如 grp [1,1,2,2,3,3,3] 给出 [[1,1],[2,2],[3, 3,3]]。我认为从那里你可以自己找到解决方案。

I don't want to spoil all the fun, but a group function would be helpful. Unfortunately it is defined in Data.List, so you need to write your own. One possible way would be:

-- 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] gives [[1,1],[2,2],[3,3,3]]. I think from there you can find the solution yourself.

自由范儿 2024-12-15 08:05:57

我会尝试以下操作:

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

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

I'd try the following:

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

Note that it works for any finite list that contains orderable elements, not just integers.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文