I have this code:
fromList :: Int -> [Int] -> [[Int]]
fromList y = takeWhile (not.null) . map (take y) . iterate (drop y)
An example of what this does:
-> fromList 4 [1..19]
[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16],[17,18,19]]
How can I make this code using a fold?
Here's a pretty elegant solution using foldr
:
fromList :: Int -> [a] -> [[a]]
fromList n = foldr (\v a ->
case a of
(x:xs) -> if length x < n then (v:x):xs else [v]:a
_ -> [[v]]
) []
Essentially, the accumulator is the end value, and for each value in the list it checks if there's still space to put it in an existing chunk, and if there isn't then it puts it in a new chunk. Sadly, due to using foldr
, extra elements are put on the left side instead of the right side. This can be fixed by using a slightly slower (and maybe slightly uglier) approach with foldl
(or foldl'
):
fromList :: Int -> [a] -> [[a]]
fromList _ [] = []
fromList n (x:xs) = reverse $ foldl (\a@(y:ys) v ->
if length y < n
then (y ++ [v]):ys
else [v]:a
) [[x]] xs