我希望获得最少两个可能的值,或者如果一个没有得到非任何一个,或者如果两个输入都没有则不返回任何值。我可以编写一个简单的函数来执行此操作,但我怀疑有一种方法可以在不编写自定义函数的情况下执行此操作。对不起,如果这是一个小问题,但有比使用这个自定义函数更简单的方法吗?
minMaybe :: Ord a => Maybe a -> Maybe a -> Maybe a
minMaybe Nothing b = b
minMaybe a Nothing = a
minMaybe (Just a) (Just b) = Just $ min a b
答案 0 :(得分:17)
可以使用Control.Applicative
中的运算符来满足规范。
myMin :: Ord x => Maybe x -> Maybe x -> Maybe x
myMin a b = min <$> a <*> b <|> a <|> b
<|>
Maybe
实现“偏好”
Nothing <|> b = b
a <|> _ = a
事情是
min <$> Just a <*> Just b = Just (min a b)
但
min <$> Just a <*> Nothing = Nothing
导致了这个问题的一些错误答案。使用<|>
可以让您在可用时更喜欢计算出的min
值,但只有一个值Just
时才能恢复。
但是你应该问一下以这种方式使用Maybe
是否合适。由于Monoid
实例的不光彩的例外,Maybe
被设置为模拟容易出错的计算。你在这里拥有的是具有“top”元素的现有Ord
的扩展名。
data Topped x = Val x | Top deriving (Show, Eq, Ord)
您会发现min
的{{1}}正是您所需要的。将类型视为数据的表示,而不是结构数据的设备,这是很好的。 Topped x
通常代表某种失败,因此为您的目的使用不同类型可能更好。
答案 1 :(得分:3)
您无法使用Applicative
或Monad
实例,因为这些上下文中的任何Nothing
都会使您的总结果为Nothing
。话虽如此,“simpler”一词是高度自以为是,你的功能很好。
答案 2 :(得分:3)
您可以使用Alternative
的{{1}}实例编写它:
Maybe
或者,您可以使用minMaybe a b = liftA2 min a b <|> a <|> b
作为默认值,因此它始终会选择另一个:
maxBound
但我不建议这样做。
答案 3 :(得分:2)
问题是关于提升功能min :: Ord a ⇒ a → a → a
与Maybe
的上下文一起使用。它具有关联性,因此Semigroup
实例可以完全满足您的要求:
min' :: forall a. Ord a => Maybe a -> Maybe a -> Maybe a
min' = coerce ((<>) @(Maybe (Min a)))
这需要ScopedTypeVariables
和TypeApplications
。 coerce
来自Data.Coerce
。下面提供了更多的老式解决方案。但是,以上版本应具有更高的性能:coerce
在运行时不存在。尽管GHC可以消除fmap
,但不能保证:
min'' :: Ord a => Maybe a -> Maybe a -> Maybe a
min'' x y = fmap getMin (fmap Min x <> fmap Min y)
P.S。我会说你的解决方案很好。
答案 4 :(得分:0)
我认为radomaj有一个good idea。
import Data.Ord (Down (..))
import Data.Function (on)
minMaybes mx my =
getDown <$> (max `on` fmap Down) mx my
getDown (Down x) = x
我们使用max
比Just
更喜欢Nothing
,然后使用Down
,因此如果两者均为Just
,我们实际上得到的最小值。
这是另一种类似的方法,看起来似乎更干净。可以使用Maybe
将额外的最小值Nothing
附加到任意Ord
类型的方法。我们可以编写自己的类型以增加最大值:
data AddMax a = TheMax | Plain a deriving Eq
instance Ord a => Ord (AddMax a) where
TheMax <= Plain _ = False
_ <= TheMax = True
Plain a <= Plain b = a <= b
maybeToAddMax :: Maybe a -> AddMax a
maybeToAddMax = maybe TheMax Plain
addMaxToMaybe :: AddMax a -> Maybe a
addMaxToMaybe TheMax = Nothing
addMaxToMaybe (Plain a) = Just a
现在您可以写
minMaybes mx my = addMaxToMaybe $
(min `on` maybeToAddMax) mx my
您还可以拉一下手脚:
{-# LANGUAGE ScopedTypeVariables, TypeApplications #-}
import Data.Ord
import Data.Function
import Data.Coerce
newtype AddMax a = AddMax {unAddMax :: Down (Maybe (Down a))}
deriving (Eq, Ord)
现在
minMaybes :: forall a. Ord a => Maybe a -> Maybe a -> Maybe a
minMaybes = coerce (min @(AddMax a))