Reputation: 770
I'm trying to look for pairs of elements in a list, assuming that they are the only pair in the list, and there are no more than 3 identical consecutive elements.
I have a function that takes in a list, and returns the index of the first element of the pair, if there is any. If not, then it returns -1
searchForPairs xs = searchHelp xs ((genericLength xs) - 1)
where searchHelp xs n
| searchHelp xs 0 = -1 -- no pairs found
| (xs !! n) == (xs !! (n - 1)) = n
| otherwise = searchHelp xs n-1
For some reason, it is returning the error:
Couldn't match expected type `Bool' with actual type `Int'
In the expression: n
In an equation for `searchHelp':
searchHelp xs n
| searchHelp xs 0 = - 1
| (xs !! n) == (xs !! (n - 1)) = n
| otherwise = searchHelp xs n - 1
In an equation for `searchForPairs':
searchForPairs xs
= searchHelp xs ((genericLength xs) - 1)
where
searchHelp xs n
| searchHelp xs 0 = - 1
| (xs !! n) == (xs !! (n - 1)) = n
| otherwise = searchHelp xs n - 1
It seems like it should work. Any ideas why it is not?
Upvotes: 0
Views: 1667
Reputation: 49246
For what it's worth, here is a somewhat idiomatic (and point-free) implementation of what you are trying to do:
searchPairs :: Eq a => [a] -> Maybe Int
searchPairs = interpret . span (uncurry (/=)) . (zip <*> tail)
where
interpret (flag, res) = if null flag then Nothing else Just $ length res
Explanation: zip <*> tail
creates a list of pairs of successive elements (using the reader Applicative type class). uncurry (/=)
tests if such a pair is made of identical elements. Finally, interpret
translates the result in a value of Maybe Int
type.
Upvotes: 1
Reputation: 4761
You have two problems. The first is in this line:
| otherwise = searchHelp xs n-1
The compiler interperets this as (searchHelp xs n) - 1
, not searchHelp xs (n-1)
, as you intended. The second problem is in you use of guards:
| searchHelp xs 0 = -1 -- no pairs found
Since searchHelp xs 0
is not a boolean expression (you wanted to use it as a pattern), the compiler rejected it. I can see two easy solutions:
searchForPairs xs = searchHelp xs ((genericLength xs) - 1)
where searchHelp xs n
| n == 0 = -1 -- no pairs found
| (xs !! n) == (xs !! (n - 1)) = n
| otherwise = searchHelp xs (n-1)
and
searchForPairs xs = searchHelp xs ((genericLength xs) - 1)
where
searchHelp xs 0 = -1 -- no pairs found
searchHelp xs n
| (xs !! n) == (xs !! (n - 1)) = n
| otherwise = searchHelp xs (n-1)
Now, unfortunately, although this works, it is terribly inefficient. This is because of your use of !!
. In Haskell, lists are linked lists, and so xs !! n
will take n steps, instead of 1. This means that the time your function takes is quadratic in the length of the list. To rectify this, you want to loop along the list forward, using pattern matching:
searchForPairs xs = searchHelp xs 0 where
searchHelp (x1 : x2 : xs) pos
| x1 == x2 = pos
| otherwise = searchHelp (x2 : xs) (pos + 1)
searchHelp _ _ = -1
Upvotes: 2
Reputation: 12749
I couldn't quite grok what you want to do, but from the code, it looks like you're trying to find two consecutive elements in a list that are equal. Instead of using !!
to index the list, you can use pattern matching to extract the first two elements of the list, check if they are equal, and continue searching the remainder (including the second element) if they are not. If the list doesn't have at least two elements, you return Nothing
searchForPairs xs = go 0 xs where
go i (x1:xs@(x2:_)) | x1 == x2 = Just i
| otherwise = go (i+1) xs
go _ _ = Nothing
Upvotes: 2
Reputation: 2477
@gereeter already explained your errors, I would just like to point out that you should not return -1
in case the answer is not found. Instead, you should return Nothing
if there is no answer and Just pos
if the answer is pos
. This protects you from many kinds of errors.
Upvotes: 2