“按大小排列的组列表”是折叠吗?

时间:2014-10-04 08:04:46

标签: haskell

我遇到了这个问题:按相同大小的数据包对列表元素进行分组,以便

> groupBy 3 [1..10] 
[[1,2,3], [4,5,6], [7,8,9], [10]]

没有什么真的很难做,但首先我很惊讶我找不到它的功能。 我的第一次尝试是

 groupBy _ [] = []
 groupBy n xs = g : groupBy n gs
              where (g, gs) = splitAt n xs

到目前为止一切都那么好,即使在无限名单上,它仍然有效。但是我不喜欢第一行groupBy _ [] = []。看起来很适合折叠,但我无法理解。

这个函数可以写成折叠还是单个衬垫?

更新

我尝试了一个班轮:

groupBy' n l = map (map snd) $ groupBy ((==) `on` fst) $ concatMap (replicate n) [1..] `zip` l

我花了10倍的时间来写下最初的尝试。

更新2

在Ganesh回答并使用unfoldrpointfree的帮助后,我推出了这个错综复杂的免费解决方案

groupBy' n = unfoldr $ listToMaybe . (ap (>>) (return.splitAt n))

1 个答案:

答案 0 :(得分:8)

你可以用一些体操作为 fold 来做,但它比unfold好得多:

 unfoldr (\xs -> if null xs then Nothing else Just (splitAt n xs)) 

[如果您还没有import Data.List

,则需要unfoldr

unfoldr :: (b -> Maybe (a, b)) -> b -> [a] 的类型是:

unfoldr

Nothing的想法是生成函数决定是停止(Just)还是继续(Just)。如果结果为n,则元组的第一个元素是输出列表的下一个元素,第二个元素再次传递给生成函数。

正如 @letrtroundabout 在对该问题的评论中指出的那样,展开更自然,因为它将输出列表元素视为彼此相似,而在折叠中输入列表元素应该同样对待。在这种情况下,需要在输入列表的每个{{1}}元素中启动一个新的子列表,这会使这更难。