Reputation: 1930
I am trying to count the elements of a list that are the same length for n and n+1.
I wrote this code:
countSec :: [[Integer]] -> Integer
countSec (x:xs) = if (length x)==(length (head xs))
then 1+(countSec xs)
else (countSec xs)
countSec [] = 0
As you may guess, it's not working. In fact, as a output I get "* Exception: Prelude.head: empty list"
countSec [[1,2][1,2],[2],[3,5],[2],[5]]
should return 2 (the first two and the last two sublists have the same length).
Any clue on what the problem can be?
Thanks
Upvotes: 1
Views: 878
Reputation: 11
There are some builtins you can use:
import Data.Function (on)
import Data.List (groupBy)
countSec = length . head . groupBy ((==) `on` length)
Upvotes: 1
Reputation: 40797
You don't handle the case where there is only one element in the list of lists, so you run into an error as soon as you call head
on xs
, which will be []
, since there's no first element of an empty list :)
You can use pattern matching to fix this:
countSec :: [[Integer]] -> Integer
countSec [] = 0
countSec [_] = 0
countSec (x : xs@(y:_))
| length x == length y = 1 + countSec xs
| otherwise = countSec xs
I used pattern matching to match two elements of the list at a time. This is just like x:(y:_)
(patterns can nest), except that we also give the name xs
to the thing we matched to y:_
; so we have
xs
x y _____\_____
\ \ \
[1] : ([2, 3] : ...)
x
is the first element of the list, y
is the second, and xs
is the list after the first element. We could just match (x:y:xs)
instead and use countSec (y:xs)
to recurse, but this would use up more memory and is more error-prone.
The _
in a pattern means that we don't care about the value at that position, and so don't want to give it a name; it's a wildcard that matches everything.
As a minor stylistic note, I also converted your if
expression into a guard; these are basically just if expressions at the level of a function clause.
You should avoid using partial1 functions like head
when pattern matching would work because of things like this — they hide errors and make code harder to read. I also suggest giving the -Wall
flag to GHC; if you wrote countSec
as I did but forgot the zero or one-element case, GHC would warn you about it:
Warning: Pattern match(es) are non-exhaustive
In an equation for `countSec': Patterns not matched: [_]
1 A function not defined for all its inputs; for instance, head
and tail
are not defined on []
, and division is not defined when the divisor is 0.
Upvotes: 6
Reputation: 183958
A different approach that calculates the length of each list only once,
countSec :: [[a]] -> Integer
countSec xss = genericLength . filter id $ zipWith (==) ls (tail ls)
where
ls = map length xss
The use of the partial function tail
is perfectly safe here, because the case where it would raise an error is caught by the shortcutting of zipWith
.
Upvotes: 3
Reputation: 4486
The tail part of your list, the xs
will be []
when you reach the end of the list, and head
will result in an error.
However, the general answer to this kind of question is "never use head
. Use pattern matching". This will instantly show what is wrong in your program:
countSec :: [[Integer]] -> Integer
countSec (x:y:xs) = if (length x)==(length y)
then 1+(countSec (y:xs))
else (countSec (y:xs))
countSec (x:[]) = ??
countSec [] = 0
Upvotes: 2