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

mb1*_*b14 6 haskell

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

> 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倍多.

更新2

继Ganesh回答和使用unfoldr以及pointfree我的帮助后,我推出了这个错综复杂的免费解决方案

groupBy' n = unfoldr $ listToMaybe . (ap (>>) (return.splitAt n))
Run Code Online (Sandbox Code Playgroud)

GS *_*ica 8

你可以用一些体操作为一个折叠,但它展开得更好:

 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输入列表的每个元素的新子列表使得这更加困难.

  • 使用listToMaybe的@ mb14很好的技巧,虽然令人困惑的编码(`ap`和`>>`,`return`来自两个不同的monad ......).`unfoldr(\ xs-> listToMaybe [splitAt n xs | _ < - xs])`更容易理解.甚至`展开$ listToMaybe.(map = << const.splitAt n)`. (2认同)