Una soluzione più elegante funzionale:
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)
Utilizza scan
di accumulare insiemi di tutti gli elementi visti finora. Quindi usa zip
per combinare ogni elemento con il set di elementi prima di esso. Infine, utilizza choose
per filtrare gli elementi presenti nel set di elementi precedentemente visti, ovvero i duplicati.
EDIT
In realtà la mia risposta originale era completamente sbagliato. In primo luogo, non vuoi duplicati nelle tue uscite. In secondo luogo, vuoi le prestazioni.
Ecco una soluzione puramente funzionale che implementa l'algoritmo che stai dopo:
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)
Questo utilizza una mappa per monitorare se ogni elemento è stato visto prima una o più volte e poi emette l'elemento se è visto essere visto solo una volta, cioè la prima volta che viene duplicato.
Ecco una versione più veloce imperativo:
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 }
Si tratta di circa 2 × più veloce di tutti gli altri tuoi risposte (al momento della scrittura).
Utilizzando un'ansa for x in xs do
per enumerare gli elementi di una sequenza è sostanzialmente più lento rispetto all'utilizzo GetEnumerator
direttamente ma si genera un Enumerator
non è significativamente più veloce rispetto all'utilizzo un'espressione calcolo con yield
.
noti che la TryGetValue
membro di Dictionary
mi permette di evitare assegnazione nel ciclo interno mutando un valore pila allocato che l'organo TryGetValue
estensione offerta da F # (e utilizzato da KVB nel suo/sua risposta) alloca sua tupla ritorno.
possibile duplicato di [Come rimuovere i duplicati in una sequenza F # senza utilizzare i riferimenti] (http://stackoverflow.com/questions/6842466/how-can-i-remove-duplicates-in-an-f-sequence -senza-utilizzo-riferimenti) – gradbot
In realtà, è l'inverso. Voglio solo i duplicati. – Daniel
Hmm, come vuoi memorizzare i valori che hai già visitato? Impostato? Dizionario? – gradbot