等效于filterM

时间:2019-03-28 15:52:23

标签: haskell

列表推导与mapfilter具有重叠的功能。 filterM满足了谓词返回包裹在monad中的Bool的情况(准确地说,是Applicative)。 mapM对于将结果包装在Applicative中的映射函数执行类似的操作。

如果您想通过列表理解来重现mapM的行为,请sequence来解决。但是如何用列表理解代替filterM?换句话说,如何在上下文中返回Bool的谓词用作列表推导中的保护措施?

下面是探讨2x2x2空间的简单示例( f ilter / m ap, f unction / c 理解, p lain / m 阳极),只有我不知道该怎么写fcm。如何fcm进行固定,使其具有与ffm相同的值?

import Control.Monad (filterM)

predicate = (>3)
convert = (*10)
predicateM = Just . predicate -- Just represents an arbitrary monad
convertM   = Just . convert
data_ = [1..5]

ffp = filter           predicate  data_
ffm = filterM          predicateM data_
fcp = [a | a <- data_, predicate  a]
fcm = [a | a <- data_, predicateM a]

mfp = map        convert           data_
mfm = mapM       convertM          data_
mcp =          [ convert  a | a <- data_ ]
mcm = sequence [ convertM a | a <- data_ ]

编辑:

请注意,名称以m结尾的版本必须使用convertMpredicateM:普通的convertpredicate在以下版本中不可用这些情况;这就是问题的全部。

背景信息

动机来自于一堆函数(这是一个简化的集合,希望可以代表)

convert   ::  a  -> r -> b
predicate ::  a  -> r -> Bool
big       :: [a] -> r -> [b]

big as r = [ convert a r | a <- as, predicate a r ]

希望根据Reader ...进行重构,并且其中一个(big)使用另一个(predicate)作为listcomp保护中的谓词。

只要将listcomp替换为mapMfilterM的组合,重构就可以正常工作。

convertR   ::  a  -> Reader r b
predicateR ::  a  -> Reader r Bool
bigR       :: [a] -> Reader r [b]

bigR as = mapM convertR =<< filterM predicateR as

这样做的麻烦在于,在现实生活中,listcomp更加复杂,并且对mapMfilterM的翻译远不够清晰。

即使谓词变成了单字母,也希望保留listcomp的动机。

编辑2

实际的listcomp更为复杂,因为它组合了多个列表中的元素。我尝试将问题的实质扩展到以下示例中,该示例与Edit 1的区别在于

  • big中的listcomp从多个列表中获取数据。
  • predicate接受多个值作为输入。
  • convert已重命名为combine,并接受多个值作为输入。
  • Reader版本的类似更改。

combine   :: r -> (a,a) -> b
predicate :: r -> (a,a) -> Bool
big, big' :: r -> [a]   -> [b]

big  r as = [ combine r (a,b) | a <- as, b <- as, predicate r (a,b) ]
big' r as = map (combine r) $ filter (predicate r) $ [ (a,b) | a <- as, b <- as ]

combineR    :: (a,a) -> Reader r b
predicateR  :: (a,a) -> Reader r Bool
bigR, bigR' :: [a]   -> Reader r [b]

bigR     = undefined
bigR' as = mapM combineR =<< filterM predicateR =<< return [ (a,b) | a <- as, b <- as ]

big'是对big的重写,其中combinepredicate从listcomp中提取。在阅读器版本中,它具有直接等效项:bigR'。因此,问题是,如何写bigR应该是

  1. 不比bigR'丑陋
  2. 尽可能合理地直接翻译big

在此阶段,我很想得出结论,bigR'会达到预期的效果。这意味着我的问题的答案是:

  • 保留用于构建笛卡尔积的listcomp
  • predicatecombine从表达式中分别移到filtermap
  • 在单声道版本中,将filtermap替换为filterMmapM(将$替换为=<<)。
  • 不使用谓词和合并器函数:listcomp与已咖喱和未咖喱版本同样有效,但map-filter组合需要对其进行咖喱。在现阶段,这可能是失去使用listcomp功能的最大代价。

2 个答案:

答案 0 :(得分:1)

就我个人而言,我无法找到一种方法来实现 just list comp,但这可能会使您更接近吗? (我列出了我所经历的一些中间步骤,因此您可以根据需要采取不同的方向。)

{-# LANGUAGE MonadComprehensions #-}

predicateM = return . (>3)
[[a | True <- predicateM a] | a <- [1,2,3,4,5]] 
  :: (Num b, Ord b, Control.Monad.Fail.MonadFail m) => [m b]
[[if b then Just a else Nothing | b <- predicateM a] | a <- [1,2,3,4,5]]
  :: (Num a, Monad m, Ord a) => [m (Maybe a)]
[[bool Nothing (Just a) b | b <- predicateM a] | a <- [1,2,3,4,5]]
  :: (Num a, Monad m, Ord a) => [m (Maybe a)]
catMaybes <$> sequence [[bool Nothing (Just a) b | b <- predicateM a] | a <- [1,2,3,4,5]]
  :: (Monad f, Num a, Ord a) => f [a]

filterM' p xs = catMaybes <$> sequence [[bool Nothing (Just a) b | b <- p a] | a <- xs]

使用ListT可能会更整洁吗?

编辑:另一种方法,尽管它会发出有关ListT给出非法实例的警告。

unListT (ListT x) = x
filterM'' p xs = unListT [a | (a,True) <- ListT $ mapM (\x -> return . (x,) =<< p x) xs]

EDIT2:好吧,我将其归结为一个理解!

filterM''' :: (Traversable m1, Monad m2, Monad m1, Alternative m1) => (b -> m2 Bool) -> m1 b -> m2 (m1 b)
filterM''' p xs = [[a | (a,b) <- mlist, b] | mlist <- mapM (\x -> return . (x,) =<< p x) xs]

EDIT3:另一种方法,因为我不确定您需要访问什么。

filterM' p xs = [x | x <- filterM p xs]

然后,嵌套理解以获得对“内部”元素类型的访问。过滤和映射,

filterMap f p xs = [[f x | x <- mlist] | mlist <- filterM p xs]

答案 1 :(得分:0)

import Control.Monad.Trans.Reader
import Control.Monad (filterM)

该问题是由于希望采取某些形式的功能而引起的

f ::  x  ->  y  ->  z ->   a                 ; f  = undefined
p ::  x  ->  y  ->  z -> Bool                ; p  = undefined
u :: [x] -> [y] -> [z] -> [a]

,并在某些情况下(在本示例中为Reader)将它们替换为相似的内容:

fR ::  x  ->  y  ->  z  -> Reader r  a        ; fR = undefined
pR ::  x  ->  y  ->  z  -> Reader r Bool      ; pR = undefined
uR :: [x] -> [y] -> [z] -> Reader r [a]

问题的核心是原始功能之一具有多输入列表理解功能,该功能使用其他功能进行组合和过滤:

u xs ys zs = [ f x y z | x <- xs, y <- ys, z <- zs, p x y z ]

该问题解决了实现等效的,上下文相关的uR函数的困难;特别是明显的使用列表理解能力的丧失及其便利。

u相当整洁地转换为结合了列表理解filtermap的实现。如下所示u'u'可以直接转换为上下文版本uR

u' xs ys zs = map  ucf  $   filter  ucp  $           cartesian xs ys zs
uR xs ys zs = mapM ucfR =<< filterM ucpR =<< return (cartesian xs ys zs)

在上面,列表理解包含在cartesian

cartesian xs ys zs = [ (x,y,z) | x <- xs, y <- ys, z <- zs ]

并且该解决方案要求不使用原始功能(pfpRfR):

ucp = uncurry3 p     ;    ucpR = uncurry3 pR
ucf = uncurry3 f     ;    ucfR = uncurry3 fR

uncurry3 :: (a -> b -> c -> r) -> (a, b, c) -> r
uncurry3 f = \(a,b,c) -> f a b c

也许这种解决方案可以使人们对monad有所了解,但是我现在没有时间考虑这一点。