groupBy具有多个测试功能

时间:2012-03-24 11:29:34

标签: list haskell

在Haskell中编写以下代码是否有更好,更简洁的方法?我尝试过使用if..else,但这比以下内容的可读性低。我想避免遍历xs列表(这是巨大的!)8次,只是将元素分成8组。来自groupBy的{​​{1}}只需要一个测试条件函数:Data.List

(a -> a -> Bool) -> [a] -> [[a]]

7 个答案:

答案 0 :(得分:5)

这只会遍历列表一次:

import Data.Functor
import Control.Monad

filterN :: [a -> Bool] -> [a] -> [[a]]
filterN ps =
    map catMaybes . transpose .
    map (\x -> map (\p -> x <$ guard (p x)) ps)

对于列表中的每个元素,map生成一个Maybe列表,每个Maybe对应一个谓词;如果元素不满足谓词,则为Nothing;如果元素满足谓词,则为Just x。然后,transpose混洗所有这些列表,以便列表按谓词而不是按元素组织,map catMaybes丢弃不满足谓词的元素的条目。

一些解释:x <$ mfmap (const x) m,而对于Maybeguard bif b then Just () else Nothing,因此x <$ guard bif b then Just x else Nothing

map也可以写为map (\x -> [x <$ guard (p x) | p <- ps])

答案 1 :(得分:4)

如果你只坚持一次遍历列表,你可以写

filterMulti :: [a -> Bool] -> [a] -> [[a]]
filterMulti fs xs = go (reverse xs) (repeat []) where
  go [] acc = acc 
  go (y:ys) acc = go ys $ zipWith (\f a -> if f y then y:a else a) fs acc

答案 2 :(得分:3)

map (\ cond -> filter (check cond) xs) [condition1, condition2, ..., condition8]

答案 3 :(得分:2)

我认为您可以使用GHC.Exts中的groupWith

如果您编写a -> b函数来分配xs其“类”中的每个元素,我相信groupWith会按照您希望的方式分割xs,只穿过一次列表。

答案 4 :(得分:1)

groupBy并没有真正做到你想要的;即使它确实接受了多个谓词函数,它也不会对列表进行任何过滤。它只是将满足某些条件的列表元素的连续运行组合在一起。即使您的过滤条件组合在一起,也涵盖了所提供列表中的所有元素,这仍然是一个不同的操作。例如,groupBy不会修改列表元素的顺序,也不会在结果中多次包含给定元素,而您的操作可以同时执行这两项操作。

此功能可以满足您的需求:

import Control.Applicative

filterMulti :: [a -> Bool] -> [a] -> [[a]]
filterMulti ps as = filter <$> ps <*> pure as

举个例子:

> filterMulti [(<2), (>=5)] [2, 5, 1, -2, 5, 1, 7, 3, -20, 76, 8]
[[1, -2, 1, -20], [5, 5, 7, 76, 8]]

答案 5 :(得分:1)

作为nietaki答案的附录(这应该是一个评论,但它太长了,所以如果他的答案是正确的,接受他的!),函数a -> b可以写成一系列嵌套{{1但是,这不是非常惯用的Haskell,也不是非常可扩展的。这可能稍好一点:

if ... then .. else

它按照它满足的第一个import Data.List (elemIndex) import GHC.Exts (groupWith) f xs = groupWith test xs where test x = elemIndex . map ($ x) $ [condition1, ..., condition8] 对每个元素进行分类(并将那些不满足任何元素的元素放入它们自己的类别中)。

condition_的文档是here。)

答案 6 :(得分:1)

第一个函数将返回“uppdated”列表的列表,第二个函数将遍历整个列表,并为每个值uppdate列表

myfilter :: a -> [a -> Bool] -> [[a]] -> [[a]]
myfilter _ []   []   = []
myfilter x f:fs l:ls | f x       = (x:l): Myfilter x fs ls
                     | otherwise = l:Myfilter x fs ls


filterall :: [a] -> [a -> Bool] -> [[a]] -> [[a]]
filterall []   _  l    = l
filterall x:xs fl l:ls = filterall xs fl (myfilter x fl l)

应使用filterall xs [condition1,condition2...] [[],[]...]

调用此方法