Bardziej eleganckie rozwiązanie funkcjonalne:
let duplicates xs =
Seq.scan (fun xs x -> Set.add x xs) Set.empty xs
|> Seq.zip xs
|> Seq.choose (fun (x, xs) -> if Set.contains x xs then Some x else None)
Używa scan
gromadzić zbiory wszystkich elementów widział do tej pory. Następnie używa zip
do łączenia każdego elementu z zestawem elementów przed nim. Wreszcie, używa choose
do odfiltrowania elementów, które są w zestawie wcześniej widzianych elementów, tj. Duplikatów.
EDIT
Właściwie mój oryginalny odpowiedź była całkowicie błędne. Po pierwsze, nie chcesz duplikatów na wyjściach. Po drugie, chcesz wydajności.
Oto czysto funkcjonalne rozwiązanie, które implementuje algorytm jesteś po:
let duplicates xs =
(Map.empty, xs)
||> Seq.scan (fun xs x ->
match Map.tryFind x xs with
| None -> Map.add x false xs
| Some false -> Map.add x true xs
| Some true -> xs)
|> Seq.zip xs
|> Seq.choose (fun (x, xs) ->
match Map.tryFind x xs with
| Some false -> Some x
| None | Some true -> None)
ta wykorzystuje mapy, aby śledzić, czy każdy element został widział raz lub wiele razy, a następnie emituje element, jeśli to widać, że był widziany tylko raz, tj. po raz pierwszy jest duplikowany.
Oto szybciej imperatyw wersja:
let duplicates (xs: _ seq) =
seq { let d = System.Collections.Generic.Dictionary(HashIdentity.Structural)
let e = xs.GetEnumerator()
while e.MoveNext() do
let x = e.Current
let mutable seen = false
if d.TryGetValue(x, &seen) then
if not seen then
d.[x] <- true
yield x
else
d.[x] <- false }
Jest to około 2 × szybciej niż którykolwiek z pozostałych odpowiedzi (w chwili pisania tego tekstu).
Stosując for x in xs do
pętli wyliczyć elementy sekwencji jest znacznie wolniejsza niż przy GetEnumerator
bezpośrednio, lecz generuje własny Enumerator
jest znacznie szybciej niż przy użyciu wyrażenia obliczeń z yield
.
Należy zauważyć, że członkiem Dictionary
TryGetValue
pozwala mi uniknąć przydziały w wewnętrznej pętli przez mutację wartość stosu przeznaczono natomiast członek TryGetValue
rozszerzenie oferowanych przez F # (i wykorzystywane przez KVB w jego/jej odpowiedzi) przydziela jej krotka powrotną.
możliwy duplikat [Jak mogę usunąć duplikaty w sekwencji F # bez użycia odnośników] (http://stackoverflow.com/questions/6842466/how-can-i-remove-duplicates-in-an-f-sequence -bez użycia-referencji) – gradbot
W rzeczywistości jest odwrotnością. Chcę tylko duplikatów. – Daniel
Hmm, jak chcesz przechowywać wartości, które już odwiedziłeś? Zestaw? Słownik? – gradbot