Haskell中部分函数的图:a - >也许b - > [a] - > [(a,b)]

时间:2014-03-10 20:50:04

标签: haskell tuples maybe

鉴于部分函数f和参数列表xs,我正在寻找定义(x, f(x))的对f列表。这似乎是一件很自然的事情,但到目前为止我还没有优雅地表达出来。我想知道在Maybe / Monad / Applicative / ...区域中是否有任何可以帮助的东西?以下是有效的,但似乎有点明确。

import Data.Maybe (mapMaybe)

graph :: (a -> b) -> [a] -> [(a, b)]
graph f = map (\x -> (x, f x))

liftMaybe :: (a, Maybe b) -> Maybe (a, b)
liftMaybe (x, Just y)  = Just (x, y)
liftMaybe (_, Nothing) = Nothing

partialgraph :: (a -> Maybe b) -> [a] -> [(a, b)]
partialgraph f = mapMaybe liftMaybe . graph f

liftMaybe是否存在其他名称?我发现了以下一些重构:

import Control.Monad (ap)

graph' :: (a -> b) -> [a] -> [(a, b)]
graph' = map . ap (,)

liftMaybe' :: (a, Maybe b) -> Maybe (a, b)
liftMaybe' (a, mb) = do
    b <- mb
    return (a, b)

liftMaybe'' :: (a, Maybe b) -> Maybe (a, b)
liftMaybe'' (a, mb) = fmap ((,) a) mb

liftMaybe''' :: (a, Maybe b) -> Maybe (a, b)
liftMaybe''' = uncurry (fmap . (,))

3 个答案:

答案 0 :(得分:12)

最简单的方法可能是使用列表理解:

partialGraph :: (a -> Maybe b) -> [a] -> [(a, b)]
partialGraph f xs = [(x, fx) | (x, Just fx) <- graph f xs]

这利用了列表推导中模式匹配的失败语义:如果模式匹配失败,则跳过该列表元素。 1

例如,

ghci> partialGraph (\x -> if even x then Just $ x `quot` 2 else Nothing) [1..10]
[(2,1),(4,2),(6,3),(8,4),(10,5)]

似乎没有在任何地方定义liftSnd :: Functor f => (a, f b) -> f (a,b)函数; uncurry $ fmap . (,)就像你要得到的一样简洁。

如果你定义

preservingF :: Functor f => (a -> f b) -> a -> f (a, b)
preservingF = liftA2 fmap (,)

然后你可以使用这个函数来定义

partialGraph :: (a -> Maybe b) -> [a] -> [(a, b)]
partialGraph = mapMaybe . preservingF

这很优雅,虽然preservingF的定义有点不透明(特别是如果你内联它)。 2


1 这只是列表monad的fail(或完全合理的mzero),它只生成计算[]

2 正如您一直无法找到liftMaybeI have been unable to find preservingF for a long time

答案 1 :(得分:3)

Hoogle没有为(a,m b) -> m (a,b)返回任何内容。所以我猜答案是否定的! 我最终在本地实现了很多类似的功能。我能想到的最接近的预定义示例是sequence

sequence :: Monad m => [m a] -> m [a]

但你仍然可以通过破解你的胜利方式(使用Data.Maybe和Control.Arrow):

graph f xs = map (second fromJust) $ filter (isJust . snd) $ zip xs $ map f xs

答案 2 :(得分:3)

liftMaybe的最简单定义是

import Data.Traversable (sequenceA)

liftMaybe :: (a, Maybe b) -> Maybe (a, b)
liftMaybe = sequenceA

sequenceA的文档可以在http://hackage.haskell.org/package/base/docs/Data-Traversable.html找到。

一般类型签名为sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a),但在这种情况下,t(,) cfMaybe

另外,partialgraph可以用traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)表示(也来自Data.Traversable):

partialgraph :: (a -> Maybe b) -> [a] -> [(a, b)]
partialgraph f = mapMaybe $ \x -> traverse f (x, x)

或者,如果你更喜欢一点点无意义:

partialgraph f = mapMaybe (traverse f . join (,))

编辑:当我写这个答案时,我没有意识到GHC 7.6.3标准库中没有定义所需的TraversableFoldable实例(尽管它们在GHC 7.8中) )。在这里,他们是@robx提供的:

instance Foldable ((,) a) where
  foldr f y (u, x) = f x y

instance Traversable ((,) a) where
  traverse f (u, x) = (,) u <$> f x