【问题标题】:Obtaining a Bool out of [Maybe Bool] which is guaranteed to contain at least one Just从保证至少包含一个 Just 的 [Maybe Bool] 中获得一个 Bool
【发布时间】:2021-11-08 21:22:30
【问题描述】:

我有一个list 类型的[Maybe SomeType] 输入和一个谓词p 类型SomeType -> Bool,我想回答这个问题“谓词p 是否适用于所有SomeType s 恰好在输入中?”.

第一部分很简单:(map . fmap) p list 的类型为 [Maybe Bool]

一个重要信息是我知道length list >= 1all isNothing list == False 都成立,所以(map . fmap) p list 中必须至少有一个Just True

但是如何从该列表中取出一个 Bool

我认为我可以利用折叠(例如通过foldl)和MaybeMonadPlus 实例,执行以下操作:

allTrueOrNothing :: [Maybe Bool] -> Bool
allTrueOrNothing = fromJust . foldl mplus mzero

但这并不完全正确,因为如果mplusJust something,则不管something 是什么,它都会返回左操作数,所以allTrueOrNothing 将返回True,即使它的输入是[Just True, Just False]

我可以完成任务的最干净/最惯用的方式是什么?

我发现我可以简单地将filter 取出Nothings,然后将and 放在一起Justs,如下所示:

allTrueOrNothing' :: [Maybe Bool] -> Bool
allTrueOrNothing' = all fromJust . filter (fmap not isNothing)

但我更想知道是否有办法让 Maybe Bools 的行为像 Monoid 一样知道其 Bool 内容。

【问题讨论】:

  • 您能在映射p 之前 获取Nothing 值吗? all p . catMaybes。由于all p [] == True,原始输入中是否至少有一个非Nothing 值无关紧要。

标签: haskell functional-programming monads maybe monadplus


【解决方案1】:

这似乎有效:

> and . catMaybes $ [Just False, Nothing, Just False]
False
> and . catMaybes $ [Just False, Nothing, Just True]
False
> and . catMaybes $ [Just True, Nothing, Just True]
True

您可以使用catMaybes 将列表转换为[Bool],并使用and 结束。

(请注意,这将在 all-Nothings 列表中返回 True,根据您的假设,这是“不可能”的情况。)

如果你绝对想使用幺半群,我想你可以这样做,但它有点麻烦。它将涉及将列表的每个元素包装在一些 newtype And = And (Maybe Bool) 中,然后定义相关的 monoid 实例,然后 mconcating 全部,最后展开。

未经测试的代码:

newtype And = And (Maybe Bool)

instance Semigroup And where
   And Nothing  <> x            = x
   x            <> And Nothing  = x
   And (Just a) <> And (Just b) = And (Just (a && b))

instance Monoid And where
   mempty = Nothing

allTrueOrNothing :: [Maybe Bool] -> Bool
allTrueOrNothing = fromMaybe False . coerce . mconcat @And . coerce

【讨论】:

  • newtype And = And (Maybe Bool)?
  • @AlexeyRomanov 我使用相关实例进行了编辑。希望现在更清楚了。
  • newtype And = And (Maybe Bool) deriving (Semigroup, Monoid) via Maybe All
【解决方案2】:

我会直接使用all

all . all :: (a -> Bool) -> [Maybe a] -> Bool

如果由于某种原因你必须有你描述的相位区分,那么你可以使用专业化and = all id

all and :: [Maybe Bool] -> Bool

【讨论】:

  • 我花了一秒钟才意识到这是可行的,因为[Maybe a] 的形式为f (g a),其中fg 都是可折叠的。聪明(也许有点过分;-),但我喜欢)。
  • 一般Foldables 是否撰写文章?
  • @chepner 是的,通过foldMap = foldMap . foldMap
  • @DanielWagner 确实可以进行类型检查,但我不确定是否存在某些可能违反的法律,具体取决于幺半群。
  • @chepner Foldable 规律不是很有趣:如果你只定义foldMap,所有规律都被其他方法的默认实现满足,因为规律只与@相关987654333@ 方法彼此。
【解决方案3】:

最干净的方式是and . catMaybes

但是您想以&amp;&amp; 的方式使用知道其Bool 内容的Monoid。那是All:

> foldMap (fmap All) [Just True,Nothing,Just False]
Just (All {getAll = False})

> foldMap (fmap All) [Just True,Nothing,Just True]
Just (All {getAll = True})

【讨论】:

  • 接受这一点,因为它更密切地解决了这个问题,恕我直言,尽管利用MaybeFoldable 实例的答案也很棒。
  • @Enlico 你的意思是“除了”,而不是“尽管”?但无论如何它没有。它使用instance Monoid a =&gt; Monoid (Maybe a)instance Foldable []foo :: (a -&gt; Maybe All) -&gt; [a] -&gt; Maybe All ; foo = foldMap 也可以。 (参见foldMap :: (Monoid m, Foldable t) =&gt; (a -&gt; m) -&gt; t a -&gt; m)。 :)
  • 不,我的意思是尽管。问题是我在 answer 之前错误地写了 the 而不是 another。我指的是丹尼尔瓦格纳的回答。
猜你喜欢
  • 1970-01-01
  • 2016-07-24
  • 1970-01-01
  • 2022-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-24
  • 1970-01-01
相关资源
最近更新 更多