0

I'm trying to make a function in haskell to know if all the elements in a list of list have the same length. (I've search answers in previous posts but none of them works).

sameLength :: [[t]] -> String
sameLength [] = "Empty list"
sameLength [[items]]
    | and $ map (\x -> length x == (length $ head [[items]])) [[items]] = "Same length"
    | otherwise = "Not the same length"

The problem is that it doesn't work :

*Main> :l test.hs
[1 of 1] Compiling Main             ( test.hs, interpreted )
Ok, modules loaded: Main.
*Main> sameLength []
"Empty list"
*Main> sameLength [[1,2],[3,4]]
"*** Exception: test.hs:(2,1)-(5,39): Non-exhaustive patterns in function sameLength

*Main> sameLength [[1,2]]
"*** Exception: test.hs:(2,1)-(5,39): Non-exhaustive patterns in function sameLength

I don't really see where is the problem. It treat the case in which the parameter is an empty list and in which it is not. Am I wrong ? Did i miss something ?

Thanks for your help :)

Prygan
  • 95
  • 1
  • 9

2 Answers2

4

The pattern [x] matches a list containing exactly one item, x. Thus, the pattern [[items]] matches a single list containing a single item. What you want is to match all non-empty lists in the second case. But since empty lists have already been matched against, by elimination you simply want to match anything that hasn't already been matched.

sameLength :: [[t]] -> String
sameLength [] = "Empty list"
sameLength items = -- Code here
Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
3

you have too many [..] in here:

sameLength [[items]] 

(as Silvio explained really well) - try

sameLength items 

instead.

Further as a == a, you don't have to check if the length of the head is the same as the length of the head` (of course) and so I would recommend doing something like this:

sameLength :: [[a]] -> Bool
sameLength []     = True
sameLength (h:tl) = all ((length h ==) . length) tl

as I think the Bool result is just more useful and natural

how does this work?

all takes a predicate and a list and checks if the predicate holds for each element of the list - so (length h ==) . length = \xs -> length h == length xs as a predicate checks if a given list xs has the same length as the head-list h - so due to the remark above you only have to check this with the tail-list tl

remark

You can argue if all elements of the empty list should have the same length - but I think the answer should be yes ;)

examples

Prelude> sameLength [[1,2],[3,4]]
True
Prelude> sameLength [[1,2],[3,4,5]]
False
Prelude> sameLength [[1,2]]
True
Prelude> sameLength []
True

in case you are concerned about performance

(or you do not like the point-free style)

sameLength :: [[a]] -> Bool
sameLength []     = True
sameLength (h:tl) = let l = length h
                    in all (\xs -> length xs == l) tl
Random Dev
  • 51,810
  • 9
  • 92
  • 119
  • Won't the compiler optimize the [code]length h[/code] because of referential transparency? – user2682768 Mar 28 '15 at 18:51
  • @user2682768 honestly: I don't know - maybe (and tbh: I don't think it will matter in most cases) - if you want you can run some tests or look at the core-code (see here: http://book.realworldhaskell.org/read/profiling-and-optimization.html) - in any case it will not hurt (aside from beeing a bit longer) and I just wanted to add a version in non-point-free (!) style and so the line did fit nicer (and performance sounds impressive ;) ) – Random Dev Mar 28 '15 at 19:09
  • 1
    @user2682768 GHC doesn't automatically memoize functions because, in general, that would use a *lot* of memory. If you make a named binding to it though, like the last code block in this answer, it will only evaluate `length h` once because of sharing. – David Young Mar 29 '15 at 15:54
  • @DavidYoung thanks for pointing that out - I remember having read something like this somewhere but GHC seems to get more *magic* every day and so you can never be sure ;) – Random Dev Mar 29 '15 at 16:00
  • I'm pretty sure that `length h` will be evaluated only once and this is not a question of optimization but simply of the fact that this is a closure and h is not a parameter, thus `length h` won't change and is just a thunk that will be evaluated to WHNF once because of the comparison. – Jedai Mar 30 '15 at 17:04