リストの先頭から適切なセクションを取得するヘルパー関数を定義することで、これを行うことができます。何かのようなもの
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の違いを除いて、 の 2 つの再帰的なケースがほとんど同じ構造を持っていることを認識することで、上記はおそらくきれいになる可能性があります。countseen
groupNを繰り返し適用することで実装できますsplitNDistinct。
考えてみると、 withとeach の再帰呼び出しで -expressions を定義mapFst f (a,b) = (f a, b)して置き換えることができます。これにより、それらの類似性がさらに厄介になります。letgomapFst (x:) $ go count seen xsmapFst (x:) $ go (count+1) (x:seen) xs