我遇到了这个问题:按相同大小的数据包对列表元素进行分组,以便
> 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倍的时间来写下最初的尝试。
在Ganesh回答并使用unfoldr
和pointfree
的帮助后,我推出了这个错综复杂的免费解决方案
groupBy' n = unfoldr $ listToMaybe . (ap (>>) (return.splitAt n))
答案 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}}元素中启动一个新的子列表,这会使这更难。