How to nicely denote the alternative of three conditions?

假装没事ソ 提交于 2019-12-25 02:33:22

问题


Assume I have a character c :: Char . Now I want to see if it's equal to a or isDigit:

isAOrDigit = (||) <$> (=='a') <*> (isDigit)

So far so good. But now I want to see if it's equal to a, isDigit or is between d and g. Unfortunately, since || only accepts 2 arguments, I can't say (||) <$> (=='a') <*> (isDigit) <*> (`elem`['d'..'g']).

Is there any nice way to write this or do i have to fall back to this:

isACOrDigitOrDG c = c == 'a' || isDigit c || c `elem` ['d'..'g']

?


回答1:


You can use sequence to convert a [a -> Bool] to a a -> [Bool]. Then you can use or to combine that [Bool] value.

isACOrDigitOrDG = or . sequence
    [ (== 'a')
    , isDigit
    , (`elem` ['d'..'g'])
    ]



回答2:


You can lift the (||) operator:

(<||>) = liftA2 (||)

> :t (== 'a') <||> isDigit <||> (`elem` ['d'..'g'])
(== 'a') <||> isDigit <||> (`elem` ['d'..'g']) :: Char -> Bool

> (== 'a') <||> isDigit <||> (`elem` ['d'..'g']) $ 'a'
True



回答3:


Depending on your definition of "nice", you could write

isACOrDigitOrDG :: Char -> Bool
isACOrDigitOrDG = anyOf [(== 'a'), isDigit, (`elem` ['d'..'g'])]
   where anyOf fs c = or (fs <*> [c])
      -- anyOf = (or .) . (. pure) . (<*>)

or :: [Bool] -> Bool is true if any value in its input is true. The applicative instance for lists says that fs <*> xs applies each function in fs to each value in xs, and collects the results in a single list (for our purposes here, the order doesn't matter). Since we only have one Char, we create a list so that fs <*> [c] applies each predicate to the input character. or then tells us if any of those applications succeeded.

Honestly, though? For a fixed set of predicates, there's nothing wrong with

isACOrDigitOrDG c = c == 'a' || isDigit c || c `elem` ['d'..'g']

Point-free style can be hit-or-miss; sometimes it captures the idea of what you want to do better than a pointed defintion, other times it's just terse for the sake of terseness. I often write a function in both styles before deciding which one I'll want to read later.

As a third option between the two, you might take advantage of a list comprehension:

isACOrDigitOrDG c = or [f c | f <- tests]
    where tests = [(== 'a')
                  ,isDigit
                  ,(`elem` ['d'..'g'])
                  ]



回答4:


Another version that is similar to the other answers and short circuiting:

isACOrDigitOrDG x = any ($x) [(=='a'), isDigit, (`elem` ['d'..'g'])]

or if you'd rather have the slightly ridiculous pointfree version:

isACOrDigitOrDG = flip any [(=='a'), isDigit, (`elem` ['d'..'g'])] . flip ($)

or even better, just give that a name and use it:

satisfies :: [a -> Bool] -> a -> Bool
satisfies ps x = any ($x) ps

isACOrDigitOrDG = satisfies [(=='a'), isDigit, (`elem` ['d'..'g'])] 


来源:https://stackoverflow.com/questions/55854726/how-to-nicely-denote-the-alternative-of-three-conditions

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!