Home > Net >  Lazy mode of recursive function
Lazy mode of recursive function

Time:02-12

I'm going to find a solution to this problem:

Splitting given list to sublist with given sub-list length and skip length, for example:

groupEvery 3 1 [1..6] = [[1,2,3], [2,3,4], [3,4,5], [4,5,6]]
groupEvery 3 2 "abcdefghij" = [ "abc", "cde", "efg", "ghi", "ij"]

The groupEvery n p l args details are:

  • n is length of sub-list: Int
  • f is length of skip: Int
  • xs is input list: [a]

Here is my solution:

groupEvery :: Int -> Int -> [a] -> [[a]] -> [[a]]
groupEvery n p xs acc
    | length xs <= n = acc    [xs]
    | otherwise = groupEvery n p dropped (acc  [segment])
        where
            dropped = drop p xs
            segment = take n xs

But this solution is not lazy, for example take 3 Lib2.groupEvery 3 1 [1..] [] never finished. My question is about

  • Better solutions for the groupEvery function ?
  • How we can write recursive lazy function that accumulate some data ? In other words is there any structure for this kind of recursive functions that accumulate results and are lazy ?

CodePudding user response:

The main problem is that you use an accumulator and only return the list when you walked over the entire input list. Furthermore length l will walk through the entire list. You don't need to calculate the length. This is also inefficient: if the list is long it will each time make a linear run.

You can pattern match on the list and for an empty list return an empty list and for a non-empty list yield the take n xs and recurse with drop p xs.

groupEvery :: Int -> Int -> [a] -> [[a]]
groupEvery _ _ [] = []
groupEvery n p xs = take n xs : groupEvery n p (drop p xs)

This thus produces:

ghci> take 3 (groupEvery 3 1 [1..])
[[1,2,3],[2,3,4],[3,4,5]]

You can further improve the function by combining take n and drop p such that you only walk over the min n p parts of the list once. I leave that as an exercise.

  • Related