haskell分组问题

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

我想查找所有具有相同fst的对,并将它们合并,方法是将所有具有相同a的B列表附加在一起,然后丢弃不必要的对,依此类推

我得到的结果是:

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)类型的输入可能会更快。因此,

    group [("Dup", ["2", "3"]), ("Dup", ["1"]), ("Non", ["4"]), ("Dup", ["5"])]
        = [("Dup", ["2", "3", "1", "5"]), ("Non", ["4"])]
    
    这将在barkmadley的输入上产生
    [(“Dup”、[“2”、“3”、“1”、“5”])、(“非”、[“4”])]]

  • 或者,我们也可以致电:

    这将产生
    [(“Dup”、[“5”、“1”、“2”、“3”])、(“非”、[“4”])]
    ,这可能是问题,也可能不是问题。如果是,那么还有两种解决方案:

    • 首先使用以下方法反转输入:

    • Prepend(
      flip(+)
      )而不是append(
      (++)
      )(多亏了;我更喜欢这个解决方案):

    这两种定义都将导致
    组合
    输出
    [(“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:这似乎太麻烦了:)。顺便说一下,您现在可以删除import
Data.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 (++)