groupBy函数将不相邻的元素分组

时间:2018-11-19 15:13:59

标签: haskell grouping

groupBy函数根据用户定义的谓词相等时以及当它们在列表中相邻时,对列表中的元素进行分组。有没有将相邻元素分组的功能?

我处理三个变量的多项式。这是写成单项式和的多项式的示例:

p = (M (Monomial {coefficient = 1.0, powers = (1,0,0)}) :+: M (Monomial {coefficient = 1.0, powers = (0,1,0)})) :+: M (Monomial {coefficient = 1.0, powers = (1,0,0)})

这意味着p = 1.0 * x^1y^0z^0 + 1.0 * x^0y^1z^0 + 1.0 * x^1y^0z^0。第一项和第三项是可加的:它们具有相同的powers,然后我们可以将它们相加得到2.0 * x^1y^0z^0。我的目标是进行此添加,以简化p

我有一个函数,可以将这样的p转换为总和的单体物列表:

>> toListOfMonomials p
[M (Monomial {coefficient = 1.0, powers = (1,0,0)}),M (Monomial {coefficient = 1.0, powers = (0,1,0)}),M (Monomial {coefficient = 1.0, powers = (1,0,0)})]

我想对具有相同powers的单项式分类器进行分组。如果我这样做

groupBy ((==) `on` getPowers)
        (toListOfMonomials p)

然后将两个M (Monomial {coefficient = 1.0, powers = (1,0,0)})分组,因为它们不相邻。

一种解决方案是在使用groupBy之前根据能力对列表进行排序。这样,在排序列表中相邻的两个(或多个)具有相同powers的Monomal相邻。为了定义幂次(powers是整数的三元组),我首先定义一个Cantor配对函数:

cantorPairing :: (Int, Int) -> Int
cantorPairing (k1,k2) = (k1+k2)*(k1+k2+1) + 2*k2

cantorPairing' :: (Int, Int, Int) -> Int
cantorPairing' (k1,k2,k3) = cantorPairing(cantorPairing(k1,k2),k3)

然后我可以通过在Cantor配对功能下比较两个powers的值来进行比较:

groupBy ((==) `on` getPowers)
        (sortBy (compare `on` (cantorPairing' . getPowers)) (toListOfMonomials p))

这给出了理想的结果:通过这个结果,我可以轻松地将两个具有相同powers的单项式求和。

但这看起来很沉重,不是吗?是否没有groupBy函数将非相邻元素也分组?否则,是否有更快速的方法来达到期望的结果?

1 个答案:

答案 0 :(得分:1)

现在我无法想象通用的groupBy函数会比O(n ^ 2)时间更快,但是您可以使用类似的东西

groupBy2 :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy2 = go [] where
  go acc comp [] = acc
  go acc comp (h:t) =
    let (hs, nohs) = partition (comp h) t
    in go ((h:hs):acc) comp nohs

它的工作方式与常规groupBy完全相同,但是它加入了不相邻的元素类。

但是,如果您要使用on函数,该问题将变得更加简单,因为我们可能会将其结果用作地图的键:

import qualified Data.Map as M

groupOn :: (Ord b) => (a -> b) -> [a] -> [[a]]
groupOn f =
  let unpack = fmap snd . M.toList
      fld m a = case M.lookup (f a) m of
        Nothing -> M.insert (f a) [a] m
        Just as -> M.insert (f a) (a:as) m
  in unpack . foldl fld M.empty

这等效于

groupOn' f = groupBy2 ((==) `on` f)

(模排序)

顺便说一句–三胞胎和双胞胎已经定义了Ord实例,您可以像Int s一样比较它们