简单解决方案:find
和join
看起来你正在寻找Data.List.find
。 find
有类型签名
find :: (a -> Bool) -> [a] -> Maybe a
所以你可以这样做
result :: Maybe (Maybe String)
result = find isJust [tryCombination x y | x <- [1..5], y <- [1..5]]
或者,如果你不想要一个 Maybe (Maybe String)
(为什么呢?),你可以使用 Control.Monad.join
将它们折叠在一起,其签名为
join :: Maybe (Maybe a) -> Maybe a
为了让你拥有
result :: Maybe String
result = join $ find isJust [tryCombination x y | x <- [1..5], y <- [1..5]]
更高级的解决方案:asum
如果你想要一个略微更高级的解决方案,可以使用Data.Foldable.asum
,它具有以下签名:
asum :: [Maybe a] -> Maybe a
它的作用是从许多选项中选择第一个Just
值。它通过使用Maybe
的Alternative
实例来实现。 Maybe
的Alternative
实例的工作方式如下:(导入Control.Applicative
以访问<|>
运算符)
λ> Nothing <|> Nothing
Nothing
λ> Nothing <|> Just "world"
Just "world"
λ> Just "hello" <|> Just "world"
Just "hello"
换句话说,它从两个备选项中选择第一个
Just
值。想象一下在列表的每个元素之间放置
<|>
,这样就可以理解它的作用。
[Nothing, Nothing, Just "okay", Nothing, Nothing, Nothing, Just "okay"]
被转换为
Nothing <|> Nothing <|> Just "okay" <|> Nothing <|> Nothing <|> Nothing <|> Just "okay"
这正是
asum
函数所做的事情!由于
<|>
是短路运算,它只会计算到第一个
Just
值。因此,你的函数将变得非常简单。
result :: Maybe String
result = asum [tryCombination x y | x <- [1..5], y <- [1..5]]
为什么你想要这种更高级的解决方案呢?不仅代码更短;一旦你知道习惯用语(即当你熟悉 Alternative
和 asum
时),通过阅读代码的前几个字符就能更清楚地了解该函数的作用。
map fromJust . filter isJust
是来自Data.Maybe
的catMaybes
。 - Reid Barton