13

I have to write a function that flattens a list of lists.

For example flatten [] = [] or flatten [1,2,3,4] = [1,2,3,4] or flatten [[1,2],[3],4,5]] = [1,2,3,4,5]

I'm having trouble with the being able to match the type depending on what is given to the flatten function.

Here's what I have:

data A a = B a | C [a] deriving (Show, Eq, Ord)

flatten::(Show a, Eq a, Ord a)=>A a -> A a
flatten (C []) = (C [])
flatten (C (x:xs) ) = (C flatten x) ++ (C flatten xs)
flatten (B a) = (C [a])

From what I can tell the issue is that the ++ operator is expecting a list for both of its arguments and I'm trying to give it something of type A. I've added the A type so the function can either get a single element or a list of elements.

Does anyone know a different way to do this differently, or explain what I can do to fix the type error?

Lii
  • 11,553
  • 8
  • 64
  • 88
captainpirate
  • 131
  • 1
  • 1
  • 3
  • I'm not sure what exactly you want. Maybe `flatten :: A [a] -> A a; flatten (B xs) = C xs; flatten (C xss) = C (concat xss)` would help you? Basically, you can't write flatten so that it takes lists of different nesting and does different things with them, unless you wrap them into a new type and distinguish the cases by constructor. – Daniel Fischer Feb 29 '12 at 22:07
  • 1
    The type of your function should be `[[a]] -> [a]`. This means that `flatten []` is valid, and `flatten [[1,2,3,4]]` is valid, but `flatten [1,2,3,4]` is not. `[1,2,3,4]` isn't a list of lists. If you think about that and start from the beginning, getting rid of your special type, you'll find it much easier. – Dan Hulme Feb 29 '12 at 22:10
  • possible duplicate of [operation on list of lists | how](http://stackoverflow.com/questions/9477806/operation-on-list-of-lists-how) – rampion Mar 01 '12 at 02:23

4 Answers4

31

It's a bit unclear what you are asking for, but flattening a list of list is a standard function called concat in the prelude with type signature [[a]] -> [a].

If you make a data type of nested lists as you have started above, maybe you want to adjust your data type to something like this:

 data Lists a = List [a] | ListOfLists [Lists a]

Then you can flatten these to a list;

 flatten :: Lists a -> [a]
 flatten (List xs) = xs
 flatten (ListOfLists xss) = concatMap flatten xss

As a test,

 > flatten (ListOfLists [List [1,2],List [3],ListOfLists [List [4],List[5]]])
 [1,2,3,4,5]
Malin
  • 411
  • 3
  • 4
9

Firstly, the A type is on the right track but I don't think it's quite correct. You want it to be able to flatten arbitrarily nested lists, so a value of type "A a" should be able to contain values of type "A a":

data A a = B a | C [A a]

Secondly, the type of the function should be slightly different. Instead of returning a value of type "A a", you probably want it to return just a list of a, since by definition the function is always returning a flat list. So the type signature is thus:

flatten :: A a -> [a]

Also note that no typeclass constraints are necessary -- this function is completely generic since it does not look at the list's elements at all.

Here's my implementation:

flatten (B a) = [a]
flatten (C []) = []
flatten (C (x:xs)) = flatten x ++ flatten (C xs)
Mike T
  • 91
  • 1
2

this one liner will do the job. Although as it was mentioned by Malin the type signature is different:

flatten :: [[a]] -> [a]         
flatten xs = (\z n -> foldr (\x y -> foldr z y x) n xs) (:) []

simple test

frege> li = [[3,4,2],[1,9,9],[5,8]]
frege> flatten li
[3,4,2,1,9,9,5,8]
A_P
  • 331
  • 3
  • 15
  • 1
    The point is that the function should work for both simple and nested lists. Your works only for simple lists. – Lii Feb 08 '16 at 19:58
1

Flatten via list comprehension.

flatten arr = [y | x<- arr, y <- x]
windmaomao
  • 7,120
  • 2
  • 32
  • 36