List 更复杂的群体

List 更复杂的群体,list,haskell,grouping,List,Haskell,Grouping,我想以某种方式对列表进行分组,每个组都尽可能大,最多包含n个不同的值。分组是贪婪的 例如:groupn2[2,2,3,4,5,5,4,3,4,5,5]应该是[[2,2,3],[4,5,5,4],[3,4],[5],groupn3[2,2,3,4,5,4,3,4,5]应该是[[2,2,3,4],[5,5,4,3,4,5],[1] 我还没有想出一个好办法来实现这一点。你…吗?解决方案应该尽可能通用,因为我需要更多的组条件。您可以通过定义一个helper函数来实现这一点,该函数从列表的开头选取适当的部

我想以某种方式对列表进行分组,每个组都尽可能大,最多包含n个不同的值。分组是贪婪的

例如:groupn2[2,2,3,4,5,5,4,3,4,5,5]应该是[[2,2,3],[4,5,5,4],[3,4],[5],groupn3[2,2,3,4,5,4,3,4,5]应该是[[2,2,3,4],[5,5,4,3,4,5],[1]


我还没有想出一个好办法来实现这一点。你…吗?解决方案应该尽可能通用,因为我需要更多的组条件。

您可以通过定义一个helper函数来实现这一点,该函数从列表的开头选取适当的部分。差不多

splitNDistinct :: (Eq a) => Int -> [a] -> ([a],[a])
splitNDistinct n xs = go 0 [] xs
   where 
     go _ _ [] = ([], [])
     go count seen xs'@(x:xs)
      | x `elem` seen = let (taken, rest) = go count seen xs in (x:taken, rest)
      | count /= n = let (taken, rest) = go (count+1) (x:seen) xs in (x:taken, rest)   
      | otherwise = ([], xs')
这给

> splitNDistinct 1 [1, 1,2, 1,2,3, 1,2,3,4]
([1,1],[2,1,2,3,1,2,3,4])
> splitNDistinct 2 [1, 1,2, 1,2,3, 1,2,3,4]
([1,1,2,1,2],[3,1,2,3,4])
> splitNDistinct 3 [1, 1,2, 1,2,3, 1,2,3,4]
([1,1,2,1,2,3,1,2,3],[4])
> splitNDistinct 4 [1, 1,2, 1,2,3, 1,2,3,4]
([1,1,2,1,2,3,1,2,3,4],[])
上面的函数记录它以前看到的元素的数量和内容,然后仅在它以前看到过新元素或者新元素有空间时才获取新元素

通过认识到go的两个递归案例具有几乎相同的结构,除了在递归调用中看到的值计数和中的差异之外,可以整理上述内容。但是,因子分解很容易使函数变得不那么干净

groupN可以通过重复应用splitndistint来实现


只要想一想,就可以定义mapFst f a,b=f a,b,并用mapFst x:$go count seen xs和mapFst x:$go count+1 x:seen xs分别替换go递归调用中的let表达式,这使得它们的相似性更加恼人。

您可以通过定义一个helper函数来实现这一点,从列表的开头选取适当的部分。差不多

splitNDistinct :: (Eq a) => Int -> [a] -> ([a],[a])
splitNDistinct n xs = go 0 [] xs
   where 
     go _ _ [] = ([], [])
     go count seen xs'@(x:xs)
      | x `elem` seen = let (taken, rest) = go count seen xs in (x:taken, rest)
      | count /= n = let (taken, rest) = go (count+1) (x:seen) xs in (x:taken, rest)   
      | otherwise = ([], xs')
这给

> splitNDistinct 1 [1, 1,2, 1,2,3, 1,2,3,4]
([1,1],[2,1,2,3,1,2,3,4])
> splitNDistinct 2 [1, 1,2, 1,2,3, 1,2,3,4]
([1,1,2,1,2],[3,1,2,3,4])
> splitNDistinct 3 [1, 1,2, 1,2,3, 1,2,3,4]
([1,1,2,1,2,3,1,2,3],[4])
> splitNDistinct 4 [1, 1,2, 1,2,3, 1,2,3,4]
([1,1,2,1,2,3,1,2,3,4],[])
上面的函数记录它以前看到的元素的数量和内容,然后仅在它以前看到过新元素或者新元素有空间时才获取新元素

通过认识到go的两个递归案例具有几乎相同的结构,除了在递归调用中看到的值计数和中的差异之外,可以整理上述内容。但是,因子分解很容易使函数变得不那么干净

groupN可以通过重复应用splitndistint来实现

只要想一想,就可以定义mapFst f a,b=f a,b,并用mapFst x:$go count seen xs和mapFst x:$go count+1 x:seen xs分别替换go递归调用中的let表达式,这使得它们的相似性更加令人讨厌。

编辑: 正如dbaupp所说,我回答了一个不同的、更简单的问题。正确的理解会产生错误

import Data.List
import qualified Data.Set as S

groupN :: Ord a => Int -> [a] -> [[a]]
groupN n (h:t) = reverse . fmap reverse . fst $
                 foldl' add ([[h]], S.singleton h) t
  where insHead (l:t) i = (i:l):t
        add (l, s) i
          | i `S.member` s = (insHead l i, s)
          | S.size s == n  = ([i]:l, S.singleton i)
          | True           = (insHead l i, S.insert i s)
我认为这是正确且相当简洁的,对于m组的唯一值和长度为n的输入列表,它在相对于对数m的输入的线性时间内运行;理论上最大的问题是使用具有恒定时间插入和查找的数据结构,我认为dbaupp是在Omn中运行的。然而,我确实通过使用集合来强化条件Eq a到Ord a,并牺牲懒惰

错误代码:

import Data.List

groupN :: Eq a => Int -> [a] -> [[a]]
groupN n = concatN n . group
  where concatN n l = case splitAt n l of
          (l, [])  -> return $ concat l
          (l1, l2) -> (concat l1):(concatN n l2)
您可以使用genericSplitAt将Int放宽为整数。

编辑: 正如dbaupp所说,我回答了一个不同的、更简单的问题。正确的理解会产生错误

import Data.List
import qualified Data.Set as S

groupN :: Ord a => Int -> [a] -> [[a]]
groupN n (h:t) = reverse . fmap reverse . fst $
                 foldl' add ([[h]], S.singleton h) t
  where insHead (l:t) i = (i:l):t
        add (l, s) i
          | i `S.member` s = (insHead l i, s)
          | S.size s == n  = ([i]:l, S.singleton i)
          | True           = (insHead l i, S.insert i s)
我认为这是正确且相当简洁的,对于m组的唯一值和长度为n的输入列表,它在相对于对数m的输入的线性时间内运行;理论上最大的问题是使用具有恒定时间插入和查找的数据结构,我认为dbaupp是在Omn中运行的。然而,我确实通过使用集合来强化条件Eq a到Ord a,并牺牲懒惰

错误代码:

import Data.List

groupN :: Eq a => Int -> [a] -> [[a]]
groupN n = concatN n . group
  where concatN n l = case splitAt n l of
          (l, [])  -> return $ concat l
          (l1, l2) -> (concat l1):(concatN n l2)

您可以使用genericSplitAt将Int放宽为整数。

n个元素表示n个不同的元素?我是指n个不同的值。我已经编辑了这个问题,谢谢。所谓n个元素,你是指n个不同的元素?我是指n个不同的值。我已经编辑了这个问题,谢谢。我真的很喜欢这个,比我丑陋的解决方案好多了。。不幸的是,它不太管用。groupn2[1,2,1]=[[1,2],[1]]但是应该是[[1,2,1]],对于n>=2,groupN[1,2]=[[1],[2]]应该是[[1,2]]的时候。在问题中的一些例子上,它也失败了。我的错误——我简化了问题,这给出了n个组,而不是n个不同的组。即使按照这个标准,l,[]->l应该是l,[]->返回$concat l。我真的很喜欢这个,比我丑陋的解决方案好多了。。不幸的是,它不太管用。groupn2[1,2,1]=[[1,2],[1]]但是应该是[[1,2,1]],对于n>=2,groupN[1,2]=[[1],[2]]应该是[[1,2]]的时候。在问题中的一些例子上,它也失败了。我的错误——我简化了问题,这给出了n个组,而不是n个不同的组。即使按照这个标准,l,[]->l应该是l,[]->返回$concat l。