9

给定一个偏函数f和一个参数列表,xs我正在寻找定义的对列表。这似乎是一件很自然的事情,但到目前为止我还没有优雅地表达它。我想知道 Maybe/Monad/Applicative/... 区域是否有什么可以提供帮助的?以下工作,但似乎有点明确。(x, f(x))f

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 . (,))
4

3 回答 3

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而 就像 你 一直 找不到liftMaybe,我 也 找preservingF了 很久.

于 2014-03-10T21:25:15.773 回答
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
于 2014-03-10T21:14:52.060 回答
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
于 2014-03-11T04:26:45.870 回答