如何在相同参数上重写条件(提前终止)的组合?
假设我有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)
。
谢谢
答案 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} }}。这可能是也可能不是你想要的;要做一些在Nothing
和Just 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