Warum ist die Verwendung einer Sequenz in diesem Beispiel so viel langsamer als die Verwendung einer Liste?

Hintergrund: Ich habe eine Folge zusammenhängender Daten mit Zeitstempel. Die Datensequenz weist Lücken auf, einige sind groß, andere weisen nur einen einzigen fehlenden Wert auf.
Wann immer das Loch nur ein einzelner fehlender Wert ist, möchte ich die Löcher mit einem Dummy-Wert patchen (größere Löcher werden ignoriert).

Ich möchte die verzögerte Erzeugung der gepatchten Sequenz verwenden und verwende daher Seq.unfold.

Ich habe zwei Versionen der Methode gemacht, um die Löcher in den Daten zu patchen.

Der erste verbraucht dieSequenz von Daten mit Löchern und erzeugt die gepatchtenSequenz. Dies ist, was ich will, aber die Methoden laufen schrecklich langsam, wenn die Anzahl der Elemente in der Eingabesequenz über 1000 steigt, und es wird immer schlimmer, je mehr Elemente die Eingabesequenz enthält.

Die zweite Methode verbraucht aListe der Daten mit Löchern und erzeugt die gepatchtenSequenz und es läuft schnell. Dies ist jedoch nicht das, was ich will, da dies die Instanziierung der gesamten Eingabeliste im Speicher erzwingt.

Ich möchte die (sequence -> sequence) -Methode anstelle der (list -> sequence) -Methode verwenden, um zu vermeiden, dass die gesamte Eingabeliste gleichzeitig gespeichert wird.

Fragen:

1) Warum ist die erste Methode so langsam (wird mit größeren Eingabelisten immer schlechter) (Ich vermute, dass es mit dem wiederholten Erstellen neuer Sequenzen mit Seq.skip 1 zu tun hat, bin mir aber nicht sicher)

2) Wie kann ich mithilfe einer Eingabe das Patchen von Löchern in den Daten beschleunigen?Sequenz eher als eine EingabeListe?

Der Code:

open System

// Method 1 (Slow)
let insertDummyValuesWhereASingleValueIsMissing1 (timeBetweenContiguousValues : TimeSpan) (values : seq<(DateTime * float)>) =
    let sizeOfHolesToPatch = timeBetweenContiguousValues.Add timeBetweenContiguousValues // Only insert dummy-values when the gap is twice the normal
    (None, values) |> Seq.unfold (fun (prevValue, restOfValues) ->  
        if restOfValues |> Seq.isEmpty then
            None // Reached the end of the input seq
        else
            let currentValue = Seq.hd restOfValues
            if prevValue.IsNone then
                Some(currentValue, (Some(currentValue), Seq.skip 1 restOfValues  )) // Only happens to the first item in the seq
            else
                let currentTime = fst currentValue
                let prevTime = fst prevValue.Value
                let timeDiffBetweenPrevAndCurrentValue = currentTime.Subtract(prevTime)
                if timeDiffBetweenPrevAndCurrentValue = sizeOfHolesToPatch then
                    let dummyValue = (prevTime.Add timeBetweenContiguousValues, 42.0) // 42 is chosen here for obvious reasons, making this comment superfluous
                    Some(dummyValue, (Some(dummyValue), restOfValues))
                else
                    Some(currentValue, (Some(currentValue), Seq.skip 1 restOfValues))) // Either the two values were contiguous, or the gap between them was too large to patch

// Method 2 (Fast)
let insertDummyValuesWhereASingleValueIsMissing2 (timeBetweenContiguousValues : TimeSpan) (values : (DateTime * float) list) =
    let sizeOfHolesToPatch = timeBetweenContiguousValues.Add timeBetweenContiguousValues // Only insert dummy-values when the gap is twice the normal
    (None, values) |> Seq.unfold (fun (prevValue, restOfValues) ->  
        match restOfValues with
        | [] -> None // Reached the end of the input list
        | currentValue::restOfValues -> 
            if prevValue.IsNone then
                Some(currentValue, (Some(currentValue), restOfValues  )) // Only happens to the first item in the list
            else
                let currentTime = fst currentValue
                let prevTime = fst prevValue.Value
                let timeDiffBetweenPrevAndCurrentValue = currentTime.Subtract(prevTime)
                if timeDiffBetweenPrevAndCurrentValue = sizeOfHolesToPatch then
                    let dummyValue = (prevTime.Add timeBetweenContiguousValues, 42.0) 
                    Some(dummyValue, (Some(dummyValue), currentValue::restOfValues))
                else
                    Some(currentValue, (Some(currentValue), restOfValues))) // Either the two values were contiguous, or the gap between them was too large to patch

// Test data
let numbers = {1.0..10000.0}
let contiguousTimeStamps = seq { for n in numbers -> DateTime.Now.AddMinutes(n)}

let dataWithOccationalHoles = Seq.zip contiguousTimeStamps numbers |> Seq.filter (fun (dateTime, num) -> num % 77.0 <> 0.0) // Has a gap in the data every 77 items

let timeBetweenContiguousValues = (new TimeSpan(0,1,0))

// The fast sequence-patching (method 2)
dataWithOccationalHoles |> List.of_seq |> insertDummyValuesWhereASingleValueIsMissing2 timeBetweenContiguousValues |> Seq.iter (fun pair -> printfn "%f %s" (snd pair) ((fst pair).ToString()))

// The SLOOOOOOW sequence-patching (method 1)
dataWithOccationalHoles |> insertDummyValuesWhereASingleValueIsMissing1 timeBetweenContiguousValues |> Seq.iter (fun pair -> printfn "%f %s" (snd pair) ((fst pair).ToString()))

Antworten auf die Frage(2)

Ihre Antwort auf die Frage