如何在相同参数下编写多个布尔条件的短路连接?

时间:2018-04-04 00:27:21

标签: haskell

如何在相同参数上重写条件(提前终止)的组合?

假设我有3个条件

cond1 :: Maybe a -> Maybe a -> Maybe Bool
cond2 :: Maybe a -> Maybe a -> Maybe Bool
cond3 :: Maybe a -> Maybe a -> Maybe Bool

result = cond1 x y .&& cond2 x y .&& cond3 x y

其中.&&是Maybe Bool的短路操作

(.&&):: Maybe Bool -> Maybe Bool -> Maybe Bool
fa .&& fb = do a <- fa; if a then fb else return False

我正在寻找result的重写,以便获取这些条件的列表[cond1, cond2, cond3],并在提前终止或任何其他优雅建议下连续将其应用于元组(x,y)

谢谢

2 个答案:

答案 0 :(得分:6)

Maybe a类型只是一种分心 - 它们也可能是a,因为我们想要对它们进行的操作就是调用cond n

所以,我们有一个清单:

conditions :: [a -> b -> Maybe Bool]

和一个元组:

inputs :: (a, b)

由于我们可以对该列表中的条件做唯一的事情是用inputs作为参数调用它们,我们不妨首先这样做:

results :: [Maybe Bool]
results = map (($ inputs) . uncurry) conditions

并想找到一个f,这样

f results :: Maybe Bool

含义

f :: [Maybe Bool] -> Maybe Bool

该类型签名有一些有趣的功能,例如

f = fmap and . sequenceA

但这可能比你想要的短一点:如果其中一个条件返回Just False,我们仍然会评估剩余部分,看看是否有任何一个产生{{1} }}。这可能是也可能不是你想要的;要做一些在NothingJust False上短路的事情,我没有看到任何比编写使用列表的递归函数更聪明的方法。

答案 1 :(得分:2)

您可以获得与上述类似的解决方案,但在可折叠结构上进行短路,即不必占用整个列表。

(??) :: b -> b -> Bool -> b
a ?? b = \x -> if x then a else b

andM :: (Monad m) => m Bool -> m Bool -> m Bool
andM a b = a >>= \x -> (b ?? pure x) x

shortCircuitOnFalse :: (Foldable t, Monad m) => t (m Bool) -> m Bool
shortCircuitOnFalse = foldr andM (pure True)

因此,如果您有一个类似条件的功能列表(可折叠);

conditions :: [a -> b -> Maybe Bool]

以及这些函数的输入元组;

inputs :: (a, b)

然后你可以采取与上面相同的方法:

results :: [Maybe Bool]
results = map (($ inputs) . uncurry) conditions

whatYouWant :: Maybe Bool
whatYouWant = shortCircuitOnFalse results

whatYouWant的广义版本:

gWhatYouWant
  :: (Foldable t, Functor t, Monad m)
  => t (a -> b -> m Bool) -- ^ conditions
  -> (a, b) -- ^ inputs
  -> m Bool
gWhatYouWant cs is = shortCircuitOnFalse $ fmap (($ is) . uncurry) cs