Reputation: 61
So, I am trying to implement a function that, given a list of 3 elements ((Int,Int),Int)
, returns True
when on the second position is the same value for all 3 and False
otherwise.
For instance, [((1,2),7),((5,3),7),((1,9),7)]
should return True
and [((1,2),3),((5,3),3),((1,9),5)]
should return False
.
Here is my code:
bc :: [((Int,Int),Int)]
wcheck :: [((Int,Int),Int)] -> Bool
wcheck bc
| (( fst(fst bc) == fst(snd bc) ) && ( fst(snd bc) == fst(last bc) )) = True
| otherwise = False
And the error that I get:
E:\\study related\Module1\week 4\ttt.hs:55:65: error:
* Couldn't match expected type `[(a, b0)]'
with actual type `((a, b), (a, b1))'
* In the first argument of `last', namely `bc'
In the first argument of `fst', namely `(last bc)'
In the second argument of `(==)', namely `fst (last bc)'
* Relevant bindings include
bc :: ((a, b), (a, b1))
(bound at E:\\study related\Module1\week 4\ttt.hs:54:8)
wcheck :: ((a, b), (a, b1)) -> Bool
(bound at E:\\study related\Module1\week 4\ttt.hs:54:1)
|
55 | | (( fst(fst bc) == fst(snd bc) ) && ( fst(snd bc) == fst(last bc) )) = True
|
Could you please tell me why I get this error and a way to fix it? Thank you.
Upvotes: 2
Views: 447
Reputation: 460
Given that you want to test the equality of the second element of the outer pair, there are so many ways to do this, including the following.
First, fmapping snd
gives you these elements:
λ> fmap snd [((1,2),7),((5,3),7),((1,9),7)]
[7,7,7]
Now you can group them to lists of consecutive equal numbers:
λ> import Data.List
λ> (group . fmap snd) [((1,2),7),((5,3),7),((1,9),7)]
[[7,7,7]]
The values are equal if the length of this list of lists is at most one (assuming an empty list of such pairs is defined to have equal second elements):
λ> (length . group . fmap snd) [((1,2),7),((5,3),7),((1,9),7)]
1
Putting these together we can define
import Data.List
equalSnds :: Eq a => [(b, a)] -> Bool
equalSnds xs = (length . group . fmap snd) xs <= 1
So that
λ> equalSnds [((1,2),7),((5,3),7),((1,9),7)]
True
λ> equalSnds [((1,2),3),((5,3),3),((1,9),5)]
False
If you want to also test for the length of the list, you can do it separately:
wcheck :: [((Int,Int),Int)] -> Bool
wcheck xs = length xs == 3 && equalSnds xs
Upvotes: 0
Reputation: 477338
It will probably be easier if we perform simple pattern matching instead of using fst :: (a, b) -> a
, etc.
We can use a pattern ((_, x), _)
to obtain the second element from such 2-tuple wrapped in a 2-tuple.
So we can use pattern matching like:
wcheck :: [((Int,Int),Int)] -> Bool
wcheck [((_, x), _), ((_, y), _), ((_, z), _)] = x == y && y == z
wcheck _ = False
So here if the list contains three elements, we unpack the elements, and then check if the "second item"s are equal to each other. In case the pattern does not match (for a list with too few, or too much elements), we just return False
.
But a "list of three elements" does not make much sense. In case the number of elements is known at compile time, it is better to use a tuple, since in that way, the compiler can verify that you can only provide 3-tuples to that function.
In case we are interested in the second item of the tuple, we can use (_, x)
as pattern (we are not interested in the first item whatsoever):
wcheck :: [((Int,Int),Int)] -> Bool
wcheck [(_, x), (_, y), (_, z)] = x == y && y == z
wcheck _ = False
Note that we can generalize the signature with:
wcheck :: Eq c => [((a, b), c)] -> Bool
wcheck [(_, x), (_, y), (_, z)] = x == y && y == z
wcheck _ = False
Upvotes: 2
Reputation: 4360
fst
and snd
are not very nice in this situation. We can extract the bit we care about by doing a pattern match:
let (_,y) = x in ...
Now you want to do that to each element of your list (to check that every element has the same second value):
map (\(_,x) -> x)
And then you want to check that they are all equal:
allEqual :: Eq a => [a] -> Bool
allEqual [] = True
allEqual (x:xs) = all (\y->x==y) xs
This gets the first element, x
, from a list (if it exists) and checks that ever other item y
satisfies the test that x==y
So we can now write your function:
wcheck xs = allEqual (map (\(_,y) -> y) xs)
Upvotes: 2