更简洁的编写此代码的方法

16

以下模式在Haskell代码中经常出现。是否有更短的编写方式?

if pred x
then Just x
else Nothing

也许您可以发布一些上下文代码,展示它是如何被使用的?可能有一种重写正在使用它的代码的方法... - gatoatigrado
正如Petr Pudlák所指出的monadplus中的Control.Monad.Plus提供了一个名为partial的函数来实现这一点。更通用的方法是使用bool empty . pure <*> id - dfeuer
6个回答

28

你正在寻找在Control.Monad中的mfilter

mfilter :: MonadPlus m => (a -> Bool) -> m a -> m a

-- mfilter odd (Just 1) == Just 1
-- mfilter odd (Just 2) == Nothing

请注意,如果条件不依赖于 MonadPlus 内容,您可以写成:

"foo" <$ guard (odd 3) -- Just "foo"
"foo" <$ guard (odd 4) -- Nothing

使用 mfilter 重写 OP 的问题可能更糟,即:mfilter pred (Just x) - mb14

7

嗯……您正在寻找一个组合器,它需要一个 a、一个函数 a -> Bool,并返回一个 Maybe a。停!Hoogle 时间。没有完全匹配,但是 find 还是相当接近的:

find :: (a -> Bool) -> [a] -> Maybe a

我怀疑你可能找不到你需要的函数,但为什么不自己定义一个呢?

ifMaybe :: (a -> Bool) -> a -> Maybe a
ifMaybe f a | f a = Just a
ifMaybe _ _       = Nothing

3
通过使用find函数,你可以编写ifMaybe = (. return) . find。如果用mfilter代替find,就可以让它在任何MonadPlus上运行。 - hammar
@is7s:我不确定你的意思。类型将是 MonadPlus m => (a -> Bool) -> a -> m a。如果为真,则结果将是 return x,否则将是 mzero - hammar
5
+1 表示支持! 停止浪费时间在Hoogle上。请注意Landei的回答,就是简单的 mfilter,是第四个搜索结果。 - Dan Burton

5

使用:

(?:) (5>2) (Just 5,Nothing)

来自Data.Bool.HT。


哦哦哦哦哦... 这很棒。我从来没有看过那个模块。谢谢! - clintm
1
我认为它应该作为中缀使用:(5 > 2) ?: (Just 5, Nothing) - Dan Burton
5
除了节约一些字符之外,我真的看不出这种方法在if/then/else之上提供了什么,你仍然不必要地重复了“5”的术语,并且不得不提到“Nothing”。相比之下,“mfilter”解决方案更加优秀。 - Tom Crockett
дҪҝз”Ёutility-htиҪҜ件еҢ…пјҢжӮЁеҸҜд»ҘдҪҝз”ЁtoMaybeиҖҢдёҚжҳҜ(?:) => toMaybe (pred x) xгҖӮ - mb14

5
你可以使用guard来实现这个功能:
guard (pred x) >> return x

这是一种非常有用的行为,我甚至在我的代码库中定义了ensure以供一次性使用(每个人都有这样的东西,对吧?;-):

ensure p x = guard (p x) >> return x

稍微通用一些:ensure p x = x <$ guard (p x),其中<$来自于Control.Applicative。稍微更具有指向性: ensure p = ap (<$) (guard . p) :) - Ben Millwood
@benmachine 这真的更通用吗?guard已经要求MonadPlus,所以您仍然无法在非单子应用程序上使用您的ensure - Daniel Wagner
哦,好主意。如果我真的想要,我可以使它需要Alternative,但这可能不值得。然而,<$仍然更可爱。 - Ben Millwood

1
f pred x = if pred x then Just x else Nothing

根据上述定义,您可以简单地编写:

f pred x

当然,这与Daniel Wagner的ensure或FUZxxl的ifMaybe没有什么不同。但它的名称只是f,使它成为最短的,并且它的定义恰好是您提供的代码,使其最容易被证明正确。;)

一些ghci,只是为了好玩

ghci> let f pred x = if pred x then Just x else Nothing
ghci> f (5>) 2
Just 2
ghci> f (5>) 6
Nothing

如果你还没发现,这不是一个非常严肃的回答。其他人的回答更有见地,但我无法抵制“让这段代码更短”的玩笑回应。


1
通常我是非常喜欢使用通用代码的,但实际上我发现这个特定的函数在处理Maybe类型时非常有用,因此我会保留它,而不是使用guardmfilter等等。
我给它取的名字是justIf,我通常会用它来做这样的事情:
∀x. x ⊢ import Data.List
∀x. x ⊢ unfoldr (justIf (not . null . snd) . splitAt 3) [1..11]
[[1,2,3],[4,5,6],[7,8,9]]

基本上,这是一些需要在复合表达式中进行某种元素级过滤或检查的东西,因此使用Maybe来指示谓词的结果。

对于像这样的专业版本,你真的没有太多可以做的来使它更短。它已经非常简单了。在简洁和仅仅为了字符计数而压缩代码之间有一条细微的界限,对于这么简单的东西,我不会真的担心试图“改进”它...


@hammar:哈哈哈哈。使用 λ> 的普及启发了我改变我的,我猜当时这似乎是个好主意? - C. A. McCann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接