Reputation: 155
I'm writing a function where I would like to return True if a number of conditions are met. For example, consider this function which compares two strings:
test st1 st2
| st1 == st2
| "foo" `isInfixOf` st1 = True
| "baz" `isInfixOf` st2 = True
| otherwise = False
| otherwise = False
Obviously, this function is incorrect. However, I'm looking for a way to test several conditions and I wanted to know:
A) what is the clearest way to do this? B) is there an approach similar to what i have generated that uses guards?
For clarity, in my simple example above, the output of test
should be the following:
test "foobuz" "foobuz" = True
test "foobutter" "foobuz" = False
test "buz" "buz" = False
N.B. Chaining conditions together might be an option, but it gets very unreadable after only two or three tests:
test st1 st2 = st1 == st2 && "foo" `isInfixOf` s1 || "baz" `isInfixOf` s2
I was thinking there might be a way to use the Endo Monoid to test a chain of several conditionals?
Upvotes: 5
Views: 229
Reputation: 74334
While case
is usually overkill for matching on booleans, it does give you a new context for guards.
test st1 st2 = case (st1 == st2) of
True | "foo" `isInfixOf` st1 -> True
| "baz" `isInfixOf` st2 -> True
| otherwise -> False
False -> False
You can also use pairing to form more exotic case
-based nested if
s.
foo a b c = case (p1 a b, p2 b c, p3 a c) of
(True, _ , _ ) -> val1
(False, False, True) -> val2
(False, True , _ ) -> val3
Upvotes: 4
Reputation: 25763
You can combine a case expression and patterns guards:
test st1 st2 = case st1 == st2 of
True | "foo" `isInfixOf` st1 -> ...
| "baz" `isInfixOf` st2 -> ...
| otherwise -> ...
False -> ...
Upvotes: 4
Reputation: 120711
The closest you can get to the way it looks in your original code is with the MultiWayIf
extension:
{-# LANGUAGE MultiWayIf #-}
test st1 st2
| st1 == st2 = ( if
| "foo" `isInfixOf` st1 -> True
| "baz" `isInfixOf` st2 -> True
| otherwise -> False )
| otherwise = False
st1==st2
anyway)
test st1 st2 = st1==st2 && any (`isInfixOf`st1) ["foo", "baz"]
Upvotes: 4
Reputation: 27771
Using and
and or
:
test st1 st2 = and [ st1 == st2
, or ["foo" `isInfixOf` st1
,"baz" `isInfixOf` st2] ]
Using the All
and Any
monoids:
test' st1 st2 = getAll . foldMap All $
[ st1 == st2
, getAny . foldMap Any $ ["foo" `isInfixOf` st1
,"baz" `isInfixOf` st2]]
The ala
function from Control.Lens.Wrapped
can sometimes simplify the wrapping/unwrapping of newtypes, but here it doesn't save us much:
test'' st1 st2 = ala All foldMap
[st1 == st2
,ala Any foldMap ["foo" `isInfixOf` st1
,"baz" `isInfixOf` st2 ]]
Upvotes: 2
Reputation: 8136
Sebastian's suggestion of local helpers is a good one. However, the particular example you've given essentially boils down to if CONDITION then True else False
, which can be replaced with CONDITION
. So you could simply write:
main = print (fac 5)
test st1 st2 = st1 == st2 && ("foo" `isInfixOf` st1' || "baz" `isInfixOf` st2)
Another way to write this is:
main = print (fac 5)
test st1 st2
| st1 == st2 && "foo" `isInfixOf` st1 = True
| st1 == st2 && "baz" `isInfixOf` st2 = True
| otherwise = False
Upvotes: 6
Reputation: 71899
I would use local helpers.
test st1 st2
| st1 == st2 = test_after_eq st1 st2
| otherwise = False
where test_after_eq st1' st2'
| "foo" `isInfixOf` st1' = True
| "baz" `isInfixOf` st2' = True
| otherwise = False
I think that should work.
Upvotes: 4