haskell分组功能可以这样制作吗?

时间:2016-04-15 11:43:05

标签: haskell

是否有可能以某种方式使组功能与此类似:

group :: [Int] -> [[Int]]
group [] = []
group (x:[]) = [[x]]
group (x:y:ys)
    | x == y = [[x,y], ys]
    | otherwise = [[x],[y], ys]

结果应该是这样的:

group[1,2,2,3,3,3,4,1,1] ==> [[1],[2,2],[3,3,3],[4],[1,1]]

PS:我已经在寻找Data.List实现,但它对我没什么帮助。 (https://hackage.haskell.org/package/base-4.3.1.0/docs/src/Data-List.html

是否可以使组功能比Data.List实现更清晰?

或者有人可以轻松地解释Data.List实现至少吗?

4 个答案:

答案 0 :(得分:2)

来自group

Data.ListgroupBy的专用版本,它使用等于运算符==作为对元素进行分组的函数。

groupBy函数的定义如下:

groupBy                 :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy _  []           =  []
groupBy eq (x:xs)       =  (x:ys) : groupBy eq zs
                           where (ys,zs) = span (eq x) xs

它依赖于另一个函数调用span,它根据应用于列表中每个元素的函数将列表拆分为两个列表的元组。 documentation for span包含此注释,可能有助于了解其效用。

  

span p xs相当于(takeWhile p xs, dropWhile p xs)

确保您首先了解span。在REPL中稍微玩一下。

好的,现在回到groupBy。它使用span来分割列表,使用您传入的比较函数。该函数在eq中,在group函数的情况下,它是{{1} }。在这种情况下,==函数将列表拆分为两个列表:第一个匹配从列表中提取的第一个元素,其余部分匹配元组的第二个元素。

由于span以递归方式调用自身,它会将groupBy的其余结果追加到该行,直到它结束。

从视觉上看,您可以考虑span生成的值,如下所示:

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], [])

答案 1 :(得分:2)

你的想法很好,但我认为你需要定义一个辅助函数 - 类似于下面的group_loop - 来存储累积的组。 (需要使用类似的设备来定义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

最好通过预先添加新元素来累积子组,然后立即反转所有子组:

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
从那以后,你不必为了最终解决问题而不得不追回累积的小组。不管怎样,我得到了

 >>> group[1,2,2,3,3,3,4,1,1] 
 [[1],[2,2],[3,3,3],[4],[1,1]]

答案 2 :(得分:1)

另一种看待这种情况的方法是获取输入的第一个元素x并递归地分组其余部分。然后,x将被添加到分组的第一个元素之前,或者单独进入新的第一个组。一些例子:

  1. 使用[1,2,3],我们会在[[2], [3]]中为新组添加1,产生[[1], [2], [3]]
  2. 使用[1,1,2],我们会将第一个1添加到第一组[[1], [2]],并产生[[1,1], [2]]
  3. 结果代码:

    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,通过明确处理单例列表,大大简化了递归案例。

答案 3 :(得分:0)

在这里,我想出了一个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 []