F #: eliminar duplicados de una secuencia es lento

Estoy intentando escribir una función que elimina duplicados consecutivos, según lo determinado por una función de igualdad dada, de unseq<'a> pero con un giro: necesito elúltimo duplicar de una serie de duplicados para convertirlo en la secuencia resultante. Por ejemplo, si tengo una secuencia[("a", 1); ("b", 2); ("b", 3); ("b", 4); ("c", 5)]y estoy usandofun ((x1, y1),(x2, y2)) -> x1=x2 para verificar la igualdad, el resultado que quiero ver es[("a", 1); ("b", 4); ("c", 5)]. El punto de esta función es que tengo puntos de datos entrantes, donde a veces los puntos de datos tienen legítimamente la misma marca de tiempo, pero solo me importa la última, por lo que quiero eliminar los anteriores con la misma marca de tiempo. La función que he implementado es la siguiente:

let rec dedupeTakingLast equalityFn prev s = seq {
     match ( Seq.isEmpty s ) with
     | true -> match prev with 
                 | None -> yield! s
                 | Some value -> yield value
     | false ->
         match prev with 
         | None -> yield! dedupeTakingLast equalityFn (Some (Seq.head s)) (Seq.tail s) 
         | Some prevValue -> 
             if not (equalityFn(prevValue, (Seq.head s))) then 
                 yield prevValue
             yield! dedupeTakingLast equalityFn (Some (Seq.head s)) (Seq.tail s)
}

En términos de hacer el trabajo, funciona:

> [("a", 1); ("b", 2); ("b", 3); ("b", 4); ("c", 5)] 
  |> dedupeTakingLast (fun ((x1, y1),(x2, y2)) -> x1=x2) None 
  |> List.ofSeq;;
val it : (string * int) list = [("a", 1); ("b", 4); ("c", 5)]

Sin embargo, en términos de rendimiento, es un desastre:

> #time
List.init 1000 (fun _ -> 1) 
|> dedupeTakingLast (fun (x,y) -> x = y) None 
|> List.ofSeq
#time;;    
--> Timing now on    
Real: 00:00:09.958, CPU: 00:00:10.046, GC gen0: 54, gen1: 1, gen2: 1
val it : int list = [1]    
--> Timing now off

Claramente estoy haciendo algo muy tonto aquí, pero no puedo ver qué es. ¿De dónde viene el éxito de rendimiento? Me doy cuenta de que hay mejores implementaciones, pero estoy específicamente interesado en entender por quéesta La implementación es muy lenta.

EDITAR: FYI, logró una implementación decente en el estilo funcional que se basa enSeq. solo funciona. El rendimiento está bien, ya que toma aproximadamente 1.6 veces el tiempo de la implementación de estilo imperativo@gradbot a continuación que utiliza iteradores. Parece que la raíz del problema es el uso deSeq.head ySeq.tail en llamadas recursivas en mi esfuerzo original.

let dedupeTakingLastSeq equalityFn s = 
    s 
    |> Seq.map Some
    |> fun x -> Seq.append x [None]
    |> Seq.pairwise
    |> Seq.map (fun (x,y) -> 
            match (x,y) with 
            | (Some a, Some b) -> (if (equalityFn a b) then None else Some a)  
            | (_,None) -> x
            | _ -> None )
    |> Seq.choose id

Respuestas a la pregunta(8)

Su respuesta a la pregunta