简体   繁体   English

Haskell分组功能可以这样类似吗?

[英]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. groupData.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. With [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]]
  2. With [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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM