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