haskell分组问题
我想查找所有具有相同fst的对,并将它们合并,方法是将所有具有相同a的B列表附加在一起,然后丢弃不必要的对,依此类推 我得到的结果是:haskell分组问题,haskell,Haskell,我想查找所有具有相同fst的对,并将它们合并,方法是将所有具有相同a的B列表附加在一起,然后丢弃不必要的对,依此类推 我得到的结果是: group :: Ord a => [(a, [b])] -> [(a, [b])] 但很明显,这不会减少它,因为它不会把所有的东西都分组 编辑: 范例 将输出 [("a", as),("c", cs), ("c", cs3), ("b", bs),("c", cs2), ("b", bs2)] 此函数生成以下结果: import Data.Li
group :: Ord a => [(a, [b])] -> [(a, [b])]
但很明显,这不会减少它,因为它不会把所有的东西都分组
编辑:
范例
将输出
[("a", as),("c", cs), ("c", cs3), ("b", bs),("c", cs2), ("b", bs2)]
此函数生成以下结果:
import Data.List hiding (group)
group :: (Eq a) => [(a, [b])] -> [(a, [b])]
group ((s,l):rest) = (s, l ++ concatMap snd matches) : group nonmatches
where
(matches, nonmatches) = partition (\x-> fst x == s) rest
group x = x
它的工作原理是将剩余的比特过滤成两个阵营,匹配的比特和不匹配的比特。然后它组合匹配的对象,并在不匹配的对象上递归。这实际上意味着您在输入列表中的每个“键”的输出列表中都有一个元组。两种替代解决方案:
- 如注释中所述,解决此问题的最佳方法取决于输入列表元组中不同的第一个元素的数量m。对于m barkmadley的小值,使用是一种方法。但是,对于较大的值,算法的复杂度O(n*m)不是很好。在这种情况下,O(n logn)类型的输入可能会更快。因此,
这将在barkmadley的输入上产生group [("Dup", ["2", "3"]), ("Dup", ["1"]), ("Non", ["4"]), ("Dup", ["5"])] = [("Dup", ["2", "3", "1", "5"]), ("Non", ["4"])]
[(“Dup”、[“2”、“3”、“1”、“5”])、(“非”、[“4”])]]
- 或者,我们也可以致电:
这将产生
,这可能是问题,也可能不是问题。如果是,那么还有两种解决方案:[(“Dup”、[“5”、“1”、“2”、“3”])、(“非”、[“4”])]
- 首先使用以下方法反转输入:
- Prepend(
)而不是append(flip(+)
)(多亏了;我更喜欢这个解决方案):(++)
组合
输出
[(“Dup”、[“2”、“3”、“1”、“5”])、(“非”、[“4”])]
最后请注意,
combine
的所有这些定义都要求输入列表中元组的第一个元素是类的实例。barkmadley的实现只要求这些元素是的实例。因此,存在可以由他的代码处理的输入,但不能由我处理。另一种解决方案,使用折叠在地图中累积组。由于映射,这确实要求a
是Ord
的一个实例(顺便说一句,您的原始定义要求a
是Eq
的一个实例,barkmadley已将其纳入其解决方案中)
如果您非常喜欢默默无闻,请将最后一行替换为:
import qualified Data.Map as M
group :: Ord a => [(a, [b])] -> [(a, [b])]
group = M.toList . foldr insert M.empty
where
insert (s, l) m = M.insertWith (++) s l m
这省略了不必要的
m
和uncurry
将(s,l)
配对为两个参数s
和l
您能给出一个输入和输出示例吗?很清楚:不能假设元组是按第一个值排序的吗?这样做不是很容易,只需先对列表进行排序?如果元组的两个部分都来自Ord
,则可以使用sort
,或者使用sortBy
并使用比较函数。数据中存在“doubleFilter”函数。将列表列为“partition”。对列表进行分区(doubleFilter
)的选择是否会导致O(n^2)复杂性?如果是这样,那么首先对列表进行排序(参见我的解决方案)可能会将算法改进为O(n logn)。这个断言正确与否?你是正确的。您甚至可以通过编写一个自定义合并排序来提高解决方案的效率,该排序在合并阶段合并组,而不是在排序后合并。我认为它将是O(n*m),其中m是元组中第一个元素将采用的不同值的数量,因此根据输入,排序和分组版本可能更快,也可能更慢。@barkmadley:这似乎太麻烦了:)。顺便说一下,您现在可以删除importData.Monoid
语句,因为您不再使用mappend
。很好,我不知道“fromListWith”。如果成对的第一个元素是Ord的实例,那么这确实是最好的答案。combine=assocs。fromListWith(flip(++)
还可以解决反转问题mergeGroup=prod(head,concat)。解压
其中prod(f,g)x=(f$fst x,g$snd x)
是一对函数的“乘积”。
group [("Dup", ["2", "3"]), ("Dup", ["1"]), ("Non", ["4"]), ("Dup", ["5"])]
= [("Dup", ["2", "3", "1", "5"]), ("Non", ["4"])]
import Data.List (groupBy, sortBy)
combine :: (Ord a) => [(a, [b])] -> [(a, [b])]
combine = map mergeGroup . myGroup . mySort
where
mySort = sortBy (\a b -> compare (fst a) (fst b))
myGroup = groupBy (\a b -> fst a == fst b)
mergeGroup ((a, b):xs) = (a, b ++ concatMap snd xs)
import Data.Map (assocs, fromListWith)
combine :: (Ord a) => [(a, [b])] -> [(a, [b])]
combine = assocs . fromListWith (++)
import Data.List (reverse)
import Data.Map (assocs, fromListWith)
combine :: (Ord a) => [(a, [b])] -> [(a, [b])]
combine = assocs . fromListWith (++) . reverse
import Data.Map (assocs, fromListWith)
combine :: (Ord a) => [(a, [b])] -> [(a, [b])]
combine = assocs . fromListWith (flip (++))
import qualified Data.Map as M
group :: Ord a => [(a, [b])] -> [(a, [b])]
group = M.toList . foldr insert M.empty
where
insert (s, l) m = M.insertWith (++) s l m
insert = uncurry $ M.insertWith (++)