迭代Data.Set直到成功
假设我有一个函数表示一些可能失败的计算
f :: a -> Maybe b
如果我有一个列表l
,我可以找到列表中f
成功使用的第一个(从左到右扫描时)项目,以下函数findList f l
在哪里findList
findList :: (a -> Maybe b) -> [a] -> Maybe b
findList f [] = Nothing
findList f (x : xs) = case f x of
Nothing -> findList f xs
Just y -> Just y
(或使用,例如,firstJust
从extra
包)。
题。
如果我想用Set
from做同样的事情我该怎么办containers
?
也就是说,我想要一个带有签名的函数
findSet :: (a -> Maybe b) -> Set a -> Maybe b
这相当于
findSet f s = findList f (Set.toList s)
上面的行作为一个实现。但是,我不想创建中间列表Set.toList s
(即使使用惰性求值,仍然有一些不必要的开销,对吧?)。
一旦找到成功的项目,我也不想遍历整个集合,如下面的实现:
findSet f s = foldl g Nothing s
where
g Nothing x = f x
g (Just y) _ = Just y
那么有没有一种“从左到右”遍历集合、对每个成员应用可能失败的计算并在第一个成功结果处停止的好方法?
回答
我想分享一个 IRC 上有人曾经向我展示的小技巧:提前退出成功只是对提前退出错误的另一种解释。考虑到这一点,请考虑使用Either b ()
代替Maybe b
;然后
findOne :: (a -> Either b ()) -> Set a -> Either b ()
findOne = traverse_
几乎不值得命名。如果你必须有Maybe
,你可以简单地用明显的同构来包装它。
findOneMaybe :: (a -> Maybe b) -> Set a -> Maybe b
findOneMaybe f = to . traverse_ (from . f) where
from = maybe (Right ()) Left
to = either Just (const Nothing)