Ich habe diese Frage, die mich stört; es ist als neuer Abfrageoperator gekapselt, ich habe zwei Versionen davon gemacht, um zu sehen, welche besser ist. Beide machen schrecklich.
Erster Versuch; deklarativer Stil
%Vor%Zweiter Versuch: Imperativ "Rendite" Stil
%Vor%Tatsächlich ist der zweite Versuch schlechter, sowohl was die Lesbarkeit, die Kompositionalität als auch die Geschwindigkeit betrifft.
Irgendwelche Hinweise, wie Sie das optimieren können?
Ich vermute, dass das Problem, das Sie haben, mit der Tatsache zusammenhängt, dass das Endergebnis mindestens eine O (n ^ 2) -Operation ist, möglicherweise schlechter; Ich habe noch nicht alles in meinem Kopf gearbeitet.
Warum ist das? Nehmen wir an, Sie haben [1, 2, 3, 4, 5, 6] und Sie teilen das auf, was Sie für {{1, 2}, {3, 4}, {5, 6}}
Das haben Sie nicht gemacht. Du hast das tatsächlich aufgeteilt in {nimm die ersten zwei, nimm die ersten zwei und wirf sie ab und nimm die nächsten zwei, nimm die ersten zwei und wirf dann ab und nimm die nächsten zwei und wirf sie ab und nimm den dritten zwei}
Beachten Sie, wie jeder Schritt auf dem Weg das Ergebnis neu berechnet? Das liegt daran, dass das Array zwischen Aufrufen der Enumeration wechseln könnte. LINQ wurde entwickelt, um Ihnen immer aktuelle Ergebnisse zu liefern. Sie schreiben eine Abfrage, die bedeutet "Überspringe die ersten vier und wiederhole die nächsten zwei", genau das bekommst du - eine Abfrage, die diesen Code ausführt, wenn du ihn aufzählst.
Ist die Originalsequenz klein genug und schnell genug, dass Sie das Ganze in den Speicher einlesen und alles auf einmal aufteilen können, anstatt es so träge zu tun? Ist die Sequenz alternativ indizierbar? Wenn alles, was Sie bekommen, Vorwärtszugriff auf die Sequenz ist und es zu groß oder zu langsam ist, um in den Speicher auf einmal zu lesen, dann gibt es nicht viel, was Sie hier tun können. Aber wenn Sie eine oder beide dieser Eigenschaften haben, können Sie dies zumindest linear machen.
Wenn ich Ihre Frage richtig verstanden habe, versuchen Sie eine faule Implementierung eines Enumerators zu erstellen, der eine größere Sammlung von Elementen in kleinere aufzählbare Objektgruppen aufteilt.
Zum Beispiel könnte eine Folge von einer Million Zahlen in "Abschnitte" aufgeteilt werden, von denen jeder nur 100 von ihnen erzeugt, und Sie möchten, dass alles lazy gemacht wird, dh. keine 100 Gegenstände in einer Liste sammeln, bevor sie produziert werden.
Zuerst werden Ihre Versuche mehrmals über die Sammlung wiederholt, was schlecht ist, daher das Leistungsproblem.
Wenn Sie versuchen, eine reine faule Implementierung zu erstellen, sollten Sie die folgenden Probleme berücksichtigen:
Bearbeiten : Bevor ich auf meine einfache Lösung eingehe, hier ein paar Vorbehalte:
collection.Sequence(10).ToArray()
, um ein Array von Abschnitten zu erhalten. Grundsätzlich: Meine Lösung ist kein allgemeiner Zweck . Sie sollten den @LBushkin Kommentar zu MoreLinq Batch, wenn Sie dies benötigen, und ich würde zögern, meinen Code in eine Klassenbibliothek zu legen, es müsste lokal sein, wo es gebraucht wird oder in etwas umbenannt werden, das Sie deutlich vor den damit verbundenen Problemen warnt.
Hier ist eine simple Implementierung, ich bin mir ziemlich sicher, dass es hier Fehler gibt, also sollten Sie vielleicht eine Menge Unit-Testing für Edgecases implementieren:
%Vor%Ausgabe:
%Vor%Dinge, die Sie testen sollten:
Wo immer es möglich ist, versuche ich nur einmal innerhalb eines Operators durch eine Quelle zu iterieren. Wenn die Quelle etwas wie das Ergebnis eines Reverse()
-Operators ist, könnte der Aufruf von Any
, Take
und Skip
eine Menge unangenehmer Leistung verursachen.
Es ist nicht ganz klar, was Ihr Operator zu tun versucht, aber wenn Sie es tun können, ohne die Quelle mehrmals zu lesen, kann das gut helfen - obwohl es sehr davon abhängen wird, was die Eingabe ist.
Ich hatte heute eine Idee; Sieh dir das an
%Vor%Dies ist immer noch nicht super-elegant, aber zumindest die Implementierung ist sehr kurz und lesbar.
Es kann sehr interessant sein, Abfrageoperatoren über IEnumerator zu untersuchen.
Und aus praktischen Gründen
%Vor%Wie wäre es mit einer Erweiterungsmethode?
%Vor%einige Tests:
%Vor% Müssen Sie Ihre ursprüngliche Quelle aus irgendeinem Grund behalten, während Sie fortschreiten? Wenn nicht, warum verwenden Sie keine Rekursion und verwenden Sie hd :: tl style, um den Kopf zu ziehen, übergeben Sie den tl in den rekursiven Aufruf, und auf jeder geraden Zahl Rekursion verschmelzen Sie die beiden Sie in einem Abschnitt sitzen?
Mit der aktualisierten Version der experimentellen Ix-Erweiterungen könnten Sie die Operatoren Window
oder Buffer
verwenden, um ein sliding window , das sollte erreichen, was Sie suchen.
Tags und Links optimization c# linq performance linq-to-objects