Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking if all bolean elements in a list are the same in Haskell

Tags:

haskell

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?

like image 491
cheshire Avatar asked Nov 26 '18 16:11

cheshire


2 Answers

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?

like image 78
Bartek Banachewicz Avatar answered Nov 08 '22 19:11

Bartek Banachewicz


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.

like image 25
Dietrich Epp Avatar answered Nov 08 '22 17:11

Dietrich Epp