计算 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 函数吗?
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
从头开始。
您想要遍历一个序列并获得整数的最大频率。
这听起来像是折叠的工作,因为折叠会经历一个序列,在给你最终结果之前聚合一路上的值。
Foldl 的类型如上所示。我们已经可以填写其中的一些内容(我发现这可以帮助我确定我需要什么类型)
我们需要通过它折叠一些东西来获取值。我们必须跟踪当前运行和当前计数
所以现在我们可以填写更多内容:
所以我们需要一个执行聚合的函数
所以现在我们可以使用
foldl
编写该函数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.
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)
We need to fold something through that to get the value. We have to keep track of the current run and the current count
So now we can fill in a bit more:
So we want a function that does the aggregation
So now we can write the function using
foldl
as停止...胡格尔时间!
您知道 Hoogle 会告诉您函数来自哪个模块吗? Hoolging 地图 会在搜索页面上显示以下信息:
这意味着地图同时在
Prelude
和Data.List
中定义。你可以搜索其他功能,同样可以看到它们确实在 Prelude 中。您还可以查看 Haskell 2010 >标准 Prelude 或Prelude hackage 文档。
因此我们可以进行
map
、filter
和foldr
以及Prelude中的其他操作。那挺好的。让我们从 Landei 的想法开始,将列表变成列表的列表。我们应该如何实现
groupSorted
?嗯,我不知道。我们稍后再考虑一下。假设我们已经实现了它。我们如何使用它来获得正确的解决方案?我假设如果有多个正确的解决方案(如您的第二个示例中所示),则可以只选择一个正确的解决方案。在列表上使用
groupSorted
之后,我们需要做一些事情。但什么?好吧...我们应该在列表列表中找到最长的列表。正确的?这将告诉我们哪个元素在原始列表中出现最多。然后,一旦找到最长的子列表,我们就想返回其中的元素。chooseLongest
是之前的doSomething
。我们的想法是,我们想要在列表列表xs
中选择最好的列表,然后获取其中的一个元素(它的head
就可以了)。我通过创建一个更通用的函数chooseBy
来定义它,该函数使用一个函数(在本例中,我们使用length
函数)来确定最佳选择。现在我们到了“困难”的部分。折叠。
chooseBy
和groupSorted
都是折叠。我将引导您完成groupSorted
,并将chooseBy
留给您。如何编写自己的折叠
我们知道
groupSorted
是一个折叠,因为它消耗整个列表,并产生全新的东西。我们需要选择一个初始值
start
和一个步进函数step
。我们知道它们的类型,因为foldr
的类型是(a -> b -> b) -> b-> [一]-> b
,在本例中,a
是Int
(因为xs
是[Int]
,它与[a]
对齐),而我们想要最终得到的b
是[[Int]]
。现在请记住,步进函数将逐一检查列表中的元素,并使用
step
将它们融合到累加器中。我将调用当前检查的元素v
和累加器acc
。请记住,理论上,
foldr
的工作方式是从右到左。假设我们有列表[1,2,3,3]
。让我们逐步完成该算法,从最右边的3
开始并向左进行。无论
start
是什么,当我们将它与3
组合时,它最终应该是[[3]]
。我们知道这一点是因为如果groupSorted
的原始输入列表只是[3]
,那么我们需要[[3]]
作为结果。然而,它不仅仅是[3]
。现在我们假设它只是[3,3]
。[[3]]
是新的累加器,我们想要的结果是[[3,3]]
。我们应该如何处理这些输入?好吧,我们应该将
3
添加到该内部列表中。但下一步呢?在这种情况下,我们应该创建一个包含 2 的新列表。
就像上次一样,在这种情况下,我们应该创建一个新列表,其中包含 1。
至此我们已经遍历了整个输入列表,并得到了最终的结果。那么我们如何定义
step
呢?根据v
和acc
之间的比较,似乎有两种情况。在一种情况下,
v
与acc
中第一个子列表的头相同。在这种情况下,我们将v
添加到同一个子列表中。但如果情况并非如此,那么我们将v
放入其自己的列表中,并将其添加到acc
之前。那么start
应该是什么?嗯,需要特殊处理;让我们只使用[]
并为其添加一个特殊的模式匹配。现在你就得到了它。要编写折叠,您所需要做的就是确定
start
和step
是什么,然后就完成了。通过一些清理和 eta 减少:这可能不是最有效的解决方案,但它确实有效,如果您以后需要优化,您至少知道这个函数是如何工作的。
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:
This means map is defined both in
Prelude
and inData.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
, andfoldr
, 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.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).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
is thedoSomething
from before. The idea is that we want to choose the best list in the list of listsxs
, and then take one of its elements (itshead
does just fine). I defined this by creating a more general function,chooseBy
, which uses a function (in this case, we use thelength
function) to determine which choice is best.Now we're at the "hard" part. Folds.
chooseBy
andgroupSorted
are both folds. I'll step you throughgroupSorted
, and leavechooseBy
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.We need to choose an initial value,
start
, and a stepping functionstep
. We know their types because the type offoldr
is(a -> b -> b) -> b -> [a] -> b
, and in this case,a
isInt
(becausexs
is[Int]
, which lines up with[a]
), and theb
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 elementv
, and the accumulatoracc
.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 rightmost3
and working our way left.Whatever
start
is, when we combine it with3
it should end up as[[3]]
. We know this because if the original input list togroupSorted
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]]
.What should we do with these inputs? Well, we should tack the
3
onto that inner list. But what about the next step?In this case, we should create a new list with 2 in it.
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 betweenv
andacc
.In one case,
v
is the same as the head of the first sublist inacc
. In that case we prependv
to that same sublist. But if such is not the case, then we putv
in its own list and prepend that toacc
. So what shouldstart
be? Well, it needs special treatment; let's just use[]
and add a special pattern match for it.And there you have it. All you have to do to write your on fold is determine what
start
andstep
are, and you're done. With some cleanup and eta reduction: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.
我不想破坏所有的乐趣,但是
group
功能会很有帮助。不幸的是它是在Data.List
中定义的,因此您需要编写自己的。一种可能的方法是:例如
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 inData.List
, so you need to write your own. One possible way would be: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.我会尝试以下操作:
请注意,它适用于包含可排序元素的任何有限列表,而不仅仅是整数。
I'd try the following:
Note that it works for any finite list that contains orderable elements, not just integers.