Haskell `filterM`用于“Data.Map.Map”或“Data.Set.Set”等容器`
简言之:您将如何过滤单子上的Haskell `filterM`用于“Data.Map.Map”或“Data.Set.Set”等容器`,haskell,Haskell,简言之:您将如何过滤单子上的Map或Set元素 Haskell中的谓词 我可以想出两种可能的方法: a) 往返于列表和过滤器项之间(可能效率不高): b) 如果谓词本身不是一元的,但例如是与 处于状态的状态monad;然后我们可以使用Data.Map.filter(相当特殊的 案例): filterMapM2::(Monad m,Ord k)=>(v->v->Bool)->m.Map k v->StateT v m(m.Map k v) 过滤器PM2 f m=do 这两种方法具有完全不同的语义和
Map
或Set
元素
Haskell中的谓词
我可以想出两种可能的方法:
a) 往返于列表和过滤器项之间(可能效率不高):
b) 如果谓词本身不是一元的,但例如是与
处于状态的状态
monad;然后我们可以使用Data.Map.filter
(相当特殊的
案例):
filterMapM2::(Monad m,Ord k)=>(v->v->Bool)->m.Map k v->StateT v m(m.Map k v)
过滤器PM2 f m=do
这两种方法具有完全不同的语义和效率含义
当您往返于一个列表时,您允许过滤受到之前所有比较的影响,因此最终结果可能会受到访问元素的顺序的影响。然而,在第二种情况下,过滤是一个纯函数,因此无论比较的顺序如何,答案都是相同的
例如,回答问题的用户可能希望保持偶数和奇数的数量大致相同,因此用户是否喜欢特定的数字将取决于之前提供的所有数字
另一方面,这里是M.filter
的代码:
-- | /O(n)/. Filter all elements that satisfy the predicate.
filter :: (a -> Bool) -> Set a -> Set a
filter _ Tip = Tip
filter p (Bin _ x l r)
| p x = link x (filter p l) (filter p r)
| otherwise = merge (filter p l) (filter p r)
重要的是要注意代码的形状——生成的树结构很大程度上受原始树结构的影响。也许只需要少量的再平衡。这可能比使用M.fromList
从零知识重建树要高效得多
结果是,在filterM1
情况下,您应该关注进行比较的顺序。可能M.toList
给出了一个可接受的顺序,或者您希望相反。M.toList
,或
在第二种情况下,您无需在意,因此可以让M.filter
完成所有工作,并利用其数据结构知识
更新:我刚刚注意到M.toAscList
和M.fromAscList
函数,所以这个版本的filterMapM1
可能稍微高效一些:
filterMapM1 :: (Monad m, Ord k) => (v -> m Bool) -> M.Map k v -> m (M.Map k v)
filterMapM1 f m = liftM M.fromList $ filterM (f.snd) $ M.toList m
filterMapM1 f m = liftM M.fromAscList $ filterM (f.snd) $ M.toAscList m
目前我认为没有一个更通用的抽象可以直接支持filterM
。Traversable不会,因为traverse
无法更改结构的形状。我认为,从技术上讲,使用lens可以做到这一点,但文档表明,您确实不应该这样做,而且我认为它可以通过另一个结构进行往返
您可以使用类似的内容(不可编译,因为filter
不是类成员):
filterM::(可遍历c,Monad m,Applicative m)=>(a->m Bool)->ca->m(ca)
filterM p=fmap snd。过滤器fst。遍历p'
其中p'x=(,x)px
这是否比通过列表往返更高效,可能取决于结构。可以说,它没有那么干净。浏览一个列表并没有那么糟糕;在树的情况下,如果删除一些顶级节点,则很可能必须重建结构@d8d0d65b3f7cf42您能详细说明一下吗?我不知道如何使用这些来直接删除元素。脑海中浮现的唯一方法是通过可能的中间步骤。例如,在函数f::a->m(可能是a)
上使用mapM
,然后过滤掉所有Nothing
s,然后使用fromJust
去除Maybe
。这就是你的想法吗?我很抱歉这不是一条直线。这是关于编写更短/更好的代码(还是关于效率)?我确信有一些构造涉及到使用sans
来移除元素(从Data.Map
的实例中)的镜头化遍历。@d8d0d65b3f7cf42这是关于编写更短/更好的代码(还是关于效率)?我在哈斯克尔方面不是很有经验。所以,你可以说我在寻找合适的人选™ 对。但我不确定两次遍历数据(“toMaybe”、“fromJust”)是否比遍历列表的往返更有效。一个使用sans的镜头遍历例子:我不知道镜头包。但是,总的来说,这似乎是一个非常有用的工具。谢谢你指给我看。我想我找到了这个周末要做的事我做了一个基准测试(见更新),结果显示toList
,和toaslist
做得同样好;而且比在Map
的内部表示上的实现要好得多。我不知道为什么。关于不同的语义。当然,谢谢你指出这一点。不过,我会说,如果顺序很重要,那么就不应该使用地图,而应该立即使用有序的东西,比如列表。我看了lense一眼,但没有找到一个很好的方法。正如你所说,这似乎不是镜头的主要设计目的。我做了一个小的基准测试(见更新),通过列表的往返过程做得很好。
-- | /O(n)/. Filter all elements that satisfy the predicate.
filter :: (a -> Bool) -> Set a -> Set a
filter _ Tip = Tip
filter p (Bin _ x l r)
| p x = link x (filter p l) (filter p r)
| otherwise = merge (filter p l) (filter p r)
filterMapM1 f m = liftM M.fromAscList $ filterM (f.snd) $ M.toAscList m
filterM :: (Traversable c, Monad m, Applicative m) => (a -> m Bool) -> c a -> m (c a)
filterM p = fmap snd . filter fst . traverse p'
where p' x = (,x) <$> p x