是否有可能以某种方式使组功能与此类似:
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实现至少吗?
答案 0 :(得分:2)
group
的 Data.List
是groupBy
的专用版本,它使用等于运算符==
作为对元素进行分组的函数。
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,2,3]
,我们会在[[2], [3]]
中为新组添加1,产生[[1], [2], [3]]
[1,1,2]
,我们会将第一个1添加到第一组[[1], [2]]
,并产生[[1,1], [2]]
。结果代码:
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 []