I wanna check if boolean elements in a list are all True, all False, or both True and False. Here is my code for now:
data Answer3 = Just_True | Just_False | Both deriving (Eq, Ord, Show)
type A3 = Answer3
checkBoolean :: [Bool] -> A3
checkBoolean (x:x1:xs) = if (length (x:x1:xs) `mod` 2) == 0
then if (x && x1) && checkBoolean'(x1:xs)== True
then Just_True
else if (x && x1) == True
then Both
else if checkBoolean'(xs)== True
then Both
else Just_False
else if x && checkBoolean'(x1:xs) == True
then Just_True
else if x == False
then Just_False
else Both
checkBoolean':: [Bool] -> Bool
checkBoolean' (x:xs) = x && (checkBoolean'(xs))
checkBoolean' [] = True
Calling
*Main> checkBoolean [False, False, False]
Just_False
or
*Main> checkBoolean [True, True, True]
Just_True
or
*Main> checkBoolean [True, False, False]
Both
gives correct result. But
*Main> checkBoolean [False, False, True, False, True]
Just_False
and
*Main> checkBoolean [False, False, True]
Just_False
do not work as I intended. [False, False, True, False, True]
is Both, and [False, False, True]
is also Both
I know I didn't think all possible cases and thats why this doesn't work, but is there an efficient way to write this without writing this much if else statements?
Oh wow, you kinda overcomplicated it.
There's a very useful function called all
:
all :: (a -> Bool) -> [a] -> Bool
It checks all elements for a predicate, which for booleans could be just a pair of id
and not
functions, but you can also explicitly check equality to a value, which makes for pretty readable code:
checkBoolean xs =
if all (== True) xs then Just_True
else if all (== False) xs then Just_False
else Both
It's probably also a good idea to be somewhat more explicit about the empty list ([]
) case. Should it be Just_True
, Just_False
, Both
, or perhaps another value altogether?
The functions all
or any
come to mind first:
any :: Foldable t => (a -> Bool) -> t a -> Bool
all :: Foldable t => (a -> Bool) -> t a -> Bool
If we specialize for lists, we replace t
with []
, which makes this looks like:
any :: (a -> Bool) -> [a] -> Bool
all :: (a -> Bool) -> [a] -> Bool
We can compare all of the elements to the first element to see if the list contains all the same value. If any elements are different, then we know that the list is mixed.
checkBoolean (x:xs)
-- Check if any elements are not equal to the first one.
| any (/= x) xs = Both
| x = Just_True
| otherwise = Just_False
We might want to handle the degenerate case as well:
-- "Just_True" is arbitrary... you could say "Just_False", too.
checkBoolean [] = Just_True
It is worth noting that as an exercise, you find it valuable to write the function recursively, without the use of higher-order functions like all
or any
, even though they are in the prelude. Here is an alternative, tail-recursive implementation:
checkBoolean [] = Just_True
checkBoolean (x:xs) = check xs
where
check [] = if x
then Just_True
else Just_False
check (y:ys) = if x == y
then check ys
else Both
I would not expect to see this longer version in actual project code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With