列表推导与map
和filter
具有重叠的功能。 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
结尾的版本必须使用convertM
或predicateM
:普通的convert
和predicate
在以下版本中不可用这些情况;这就是问题的全部。
动机来自于一堆函数(这是一个简化的集合,希望可以代表)
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替换为mapM
和filterM
的组合,重构就可以正常工作。
convertR :: a -> Reader r b
predicateR :: a -> Reader r Bool
bigR :: [a] -> Reader r [b]
bigR as = mapM convertR =<< filterM predicateR as
这样做的麻烦在于,在现实生活中,listcomp更加复杂,并且对mapM
和filterM
的翻译远不够清晰。
即使谓词变成了单字母,也希望保留listcomp的动机。
实际的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
的重写,其中combine
和predicate
从listcomp中提取。在阅读器版本中,它具有直接等效项:bigR'
。因此,问题是,如何写bigR
应该是
bigR'
丑陋big
。在此阶段,我很想得出结论,bigR'
会达到预期的效果。这意味着我的问题的答案是:
predicate
和combine
从表达式中分别移到filter
和map
filter
和map
替换为filterM
和mapM
(将$
替换为=<<
)。答案 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
相当整洁地转换为结合了列表理解filter
和map
的实现。如下所示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 ]
并且该解决方案要求不使用原始功能(p
,f
,pR
和fR
):
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有所了解,但是我现在没有时间考虑这一点。