Search code examples
listhaskellfold

Haskell - spliting a list into several lists


Given a sorted list of tuples, return a list containing list of tuples where each list of tuples adheres to the conditions:

1) for each (a,b) and (c,d) in the list of tuples, a == c

2) the second element of each tuple must be the previous+1, so for [(a, y1), (b, y2), (c, y3)] => y2 = y1+1; y3 = y2 + 1

Example:

Input

ex = [(0,2),(1,0),(1,2),(1,3),(1,4),(2,4)]

Output

groupTogether ex = [[(0,2)], [(1,0)], [(1,2),(1,3),(1,4)],[(2,4)]]

This must be implemented using fold.

My Implementation:

groupTogether :: [(Integer,Integer)]-> [[(Integer,Integer)]]
groupTogether [] = []
groupTogether pairs@(x:xs) = foldr (\(a,b) acc -> if ( (a == fst(last(last(acc)))) && (b == fst(last(last(acc)))) ) 
                                                  then (a,b) : (last(last(acc))) 
                                                  else [(a,b)] : ((last(acc)))
                                   ) [[]] pairs

The error I am getting:

enter image description here


Solution

  • Note that when use foldr, the right-hand side elements of given list will be processed first. For example, the list:

    [(0,2),(1,0),(1,2),(1,3),(1,4),(2,4)]
    

    when processes 3rd element (1,2), the processed elements, i.e. acc

    acc = [[(1,3),(1,4)],[(2,4)]]
    

    so, the element need to be compared with (1,2) is (1,3). that is head (head acc) not last (last acc). Moreover, instead of using head, it can be accessed through pattern matching as:

    (pairs@((x, y):xs):xss)
    

    and compare to (a, b):

    a == x && b == (y - 1)
    

    and group them together if the condition is met:

    ((a, b):pairs):xss
    

    Moreover, it is more readable to define a step function instead of using anonymous function, since it need to handle the right-most element with the empty list as:

    step p [] = [[p]]
    

    Once the first element has been processed, acc = [[p]] and never to be empty list in subsequent steps and hence match the pattern defined above. Here is how the step function be defined:

    groupTogether = foldr step []
        where step p [] = [[p]]
              step p@(a, b) acc@(pairs@((x, y):xs):xss) 
                    | a == x && b == (y - 1) = (p:pairs):xss
                    | otherwise              = [p]:acc
    

    The step function is straight forward when understand how foldr operate. Finally, as a side note, the declare:

    groupTogether [] = []
    

    is not necessary. Since foldr will return its second argument when pass an empty list to groupTogether, in this example, return [].