[英]Can haskell grouping function be made similarly like that?
Is it possible to somehow make group function similarly to that: 是否可以使组功能类似于以下方式:
group :: [Int] -> [[Int]]
group [] = []
group (x:[]) = [[x]]
group (x:y:ys)
| x == y = [[x,y], ys]
| otherwise = [[x],[y], ys]
Result shoult be something like that: 结果应该是这样的:
group[1,2,2,3,3,3,4,1,1] ==> [[1],[2,2],[3,3,3],[4],[1,1]]
PS: I already looked for Data.List implementation, but it doesn't help me much. PS:我已经在寻找Data.List的实现,但是对我没有多大帮助。 ( https://hackage.haskell.org/package/base-4.3.1.0/docs/src/Data-List.html )
( https://hackage.haskell.org/package/base-4.3.1.0/docs/src/Data-List.html )
Is it possible to make group funtion more clearer than the Data.List implementation? 是否有可能使组功能比Data.List实现更清晰?
Or can somebody easily explain the Data.List implementation atleast? 还是有人可以轻松地至少解释一下Data.List实现?
group
from Data.List
is a specialized version of groupBy
which uses the equality operator ==
as the function by which it groups elements. group
从Data.List
是的专用版本groupBy
其使用等于运算符==
作为函数,通过该组中的元素。
The groupBy
function is defined like this: groupBy
函数的定义如下:
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy _ [] = []
groupBy eq (x:xs) = (x:ys) : groupBy eq zs
where (ys,zs) = span (eq x) xs
It relies on another function call span
which splits a list into a tuple of two lists based on a function applied to each element of the list. 它依赖于另一个函数调用
span
,该函数调用span
基于应用于列表每个元素的函数将一个列表分为两个列表的元组。 The documentation for span
includes this note which may help understand its utility. 有关
span
的文档中包含此注释,这可能有助于您了解其效用。
span p xs
is equivalent to(takeWhile p xs, dropWhile p xs)
span p xs
等效于(takeWhile p xs, dropWhile p xs)
Make sure you first understand span
. 确保您首先了解
span
。 Play around with it a little in the REPL. 在REPL中试一下。
Ok, so now back to groupBy
. 好的,现在回到
groupBy
。 It uses span
to split up a list, using the comparison function you pass in. That function is in eq
, and in the case of the group
function, it is ==
. 使用传入的比较函数,它使用
span
来拆分列表。该函数在eq
,对于group
函数,它是==
。 In this case, the span
function splits the list into two lists: The first of which matches the first element pulled from the list, and the remainder in the second element of the tuple. 在这种情况下,
span
函数将列表分成两个列表:第一个与从列表中拉出的第一个元素匹配,其余的与元组的第二个元素匹配。
And since groupBy
recursively calls itself, it appends the rest of the results from span
down the line until it reaches the end. 而且,由于
groupBy
递归调用自身,其追加的结果从静止span
直到它到达终点的路线。
Visually, you can think of the values produced by span
looking something like this: 在视觉上,您可以想到
span
生成的值,如下所示:
([1], [2,2,3,3,3,4,1,1])
([2,2], [3,3,3,4,1,1])
([3,3,3], [4,1,1])
([4], [1,1])
([1,1], [])
The recursive portion joins all the first elements of those lists together in another list, giving you the result of 递归部分将这些列表的所有首个元素结合在一起,形成另一个列表,为您提供以下结果:
[[1],[2,2],[3,3,3],[4],[1,1]]
Your idea is good, but I think you will need to define an ancillary function -- something like group_loop
below -- to store the accumulated group. 您的想法很好,但是我认为您需要定义一个辅助功能(类似于下面的
group_loop
)来存储累积的组。 (A similar device is needed to define span
, which the Data.List implementation uses; it is no more complicated to define group
directly, as you wanted to do.) You are basically planning to move along the original list, adding items to the subgroup as long as they match, but starting a new subgroup when something doesn't match: (需要使用类似的设备来定义Data.List实现使用的
span
;如您想要的那样,直接定义group
并不复杂。)您基本上打算沿着原始列表移动,将项目添加到子组,只要它们匹配即可,但在某些不匹配项时开始一个新的子组:
group [] = []
group (x:xs) = group_loop [x] x xs
where
group_loop acc c [] = [acc]
group_loop acc c (y:ys)
| y == c = group_loop (acc ++ [y]) c ys
| otherwise = acc : group_loop [y] y ys
It might be better to accumulate the subgroups by prepending the new element, and then reversing all at once: 最好先添加新元素,然后一次反转所有元素,以累积子组:
group [] = []
group (x:xs) = group_loop [x] x xs
where
group_loop acc c [] = [reverse acc]
group_loop acc c (y:ys)
| y == c = group_loop (y:acc) c ys
| otherwise = reverse acc : group_loop [y] y ys
since then you don't have to keep retraversing the accumulated subgroup to tack things on the end. 从那时起,您不必继续遍历累积的子组来最终解决问题。 Either way, I get
无论哪种方式,我都会
>>> group[1,2,2,3,3,3,4,1,1]
[[1],[2,2],[3,3,3],[4],[1,1]]
Another way of looking at this is to take the first element x
of the input and recursively group the rest of it. 另一种看待此问题的方法是获取输入的第一个元素
x
并将其其余部分递归分组。 x
will then either be prepended to the first element of the grouping, or go in a new first group by itself. 然后,
x
将被添加到分组的第一个元素之前,或者单独进入一个新的第一组。 Some examples: 一些例子:
[1,2,3]
, we'll add 1 to a new group in [[2], [3]]
, yielding [[1], [2], [3]]
[1,2,3]
,我们将1添加到[[2], [3]]
的新组中,产生[[1], [2], [3]]
[1,1,2]
, we'll add the first 1 to the first group of [[1], [2]]
, yielding [[1,1], [2]]
. [1,1,2]
,我们将第一个1添加到[[1], [2]]
的第一组中,产生[[1,1], [2]]
。 The resulting code: 结果代码:
group :: [Int] -> [[Int]]
group [] = []
group [x] = [[x]]
group (x:y:ys) = let (first:rest) = group (y:ys)
in if x /= y
then [x]:first:rest -- Example 1 above
else (x:first):rest -- Example 2 above
IMO, this simplifies the recursive case greatly by treating singleton lists explicitly. IMO,通过显式处理单例列表,极大地简化了递归情况。
Here, I come up with a solution with foldr
: 在这里,我想出了
foldr
的解决方案:
helper x [] = [[x]]
helper x xall@(xs:xss)
| x == head xs = (x:xs):xss
| otherwise = [x]:xall
group :: Eq a => [a] -> [[a]]
group = foldr helper []
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.