我遇到了这个问题:按相同大小的数据包对列表的元素进行分组,以便这样做
> groupBy 3 [1..10]
[[1,2,3], [4,5,6], [7,8,9], [10]]
Run Code Online (Sandbox Code Playgroud)
没有什么真的很难做,但首先我很惊讶我找不到它的功能.我的第一次尝试是
groupBy _ [] = []
groupBy n xs = g : groupBy n gs
where (g, gs) = splitAt n xs
Run Code Online (Sandbox Code Playgroud)
到目前为止这么好,它的工作原理,甚至在无限列表中.但是我不喜欢第一行groupBy _ [] = [].看起来很适合折叠,但我无法理解.
那么这个功能可以写成折叠还是单个衬垫?
我尝试一个班轮:
groupBy' n l = map (map snd) $ groupBy ((==) `on` fst) $ concatMap (replicate n) [1..] `zip` l
Run Code Online (Sandbox Code Playgroud)
我写了最初的尝试花了10倍多.
继Ganesh回答和使用unfoldr以及pointfree我的帮助后,我推出了这个错综复杂的免费解决方案
groupBy' n = unfoldr $ listToMaybe . (ap (>>) (return.splitAt n))
Run Code Online (Sandbox Code Playgroud)
你可以用一些体操作为一个折叠,但它展开得更好:
unfoldr (\xs -> if null xs then Nothing else Just (splitAt n xs))
Run Code Online (Sandbox Code Playgroud)
[ import Data.List如果你还没有,你需要]
类型unfoldr是:
unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
Run Code Online (Sandbox Code Playgroud)
想法unfoldr是生成函数决定是停止(Nothing)还是继续前进(Just).如果结果是,Just则元组的第一个元素是输出列表的下一个元素,第二个元素再次传递给生成函数.
正如@leftroundabout在对该问题的评论中指出的那样,展开更加自然,因为它将输出列表元素视为彼此相似,而在折叠中,输入列表元素应该被视为相似.在这种情况下,需要启动n输入列表的每个元素的新子列表使得这更加困难.