Warum vergleicht das Bestellen mit Linq-to-Objects Objekte mit sich selbst?

8

Betrachten Sie den folgenden einfachen Code mit LINQ OrderBy und ThenBy :

%Vor%

Dies verwendet einfach einige Vergleiche, die auf die Konsole schreiben, was sie vergleichen.

Auf meiner Hardware und Version des Framework (.NET 4.6.2) ist dies die Ausgabe:

%Vor%

Meine Frage ist: Warum würden sie ein Element von der Abfrage mit sich selbst vergleichen?

Im ersten Fall, vor dem Trennzeichen -- , führen sie vier Vergleiche durch. Zwei von ihnen vergleichen einen Eintrag mit sich selbst ("Strings: Bravo versus Bravo"). Warum?

Im zweiten Fall sollte es niemals notwendig sein, auf den Vergleich der Eigenschaften Q (Ganzzahlen) zurückzugreifen; Da es in den P -Werten keine Duplikate (in Ordinalvergleich) gibt, sollte nie ein Abbruch von ThenBy benötigt werden. Trotzdem sehen wir zweimal "Ints: 9 gegen 9". Warum verwenden Sie den ThenBy Comparer mit identischen Argumenten?

Hinweis: Jeder Vergleich muss 0 zurückgeben, wenn er etwas mit sich selbst vergleicht. Also, wenn der Algorithmus nur überprüfen will, ob wir einen Comparer richtig implementiert haben (was er sowieso nie voll machen kann), was ist dann los?

Beachten Sie: Es gibt keine Duplikate in den Elementen, die die Abfragen in meinen Beispielen ergeben.

Ich habe das gleiche Problem mit einem anderen Beispiel mit mehr Einträgen aus der Abfrage gesehen. Oben gebe ich nur ein kleines Beispiel. Dies geschieht mit einer geraden Anzahl von Elementen, die ebenfalls zurückgegeben werden.

    
Jeppe Stig Nielsen 29.08.2017, 07:39
quelle

4 Antworten

2

In der Referenzquelle der QuickSort -Methode Verwendet von OrderBy können Sie diese zwei Zeilen sehen:

%Vor%

Diese while -Schleifen laufen so lange, bis sie ein Element finden, das nicht mehr "größer" (bzw. "kleiner") ist als das, auf das x zeigt. So werden sie brechen, wenn das identische Element verglichen wird.

Ich kann es nicht mathematisch beweisen, aber ich denke, dass der Vergleich von identischen Elementen den Algorithmus komplizierter machen würde und Overhead verursachen würde, der die Leistung stärker beeinflussen würde als dieser einzelne Vergleich (Beachten Sie, dass Ihr Vergleicher so clever implementiert werden sollte, dass 0 für identische Elemente schnell zurückgegeben wird)

    
René Vogt 29.08.2017 08:18
quelle
0
  

Im ersten Fall, vor dem Trennzeichen, führen sie vier Vergleiche durch. Zwei von ihnen vergleichen einen Eintrag mit sich selbst ("Strings: Bravo versus Bravo"). Warum?

Effizienz. Sicher wäre es möglich, das Objekt selbst nicht zuerst zu überprüfen, aber das bedeutet, bei jedem Vergleich eine zusätzliche Operation durchzuführen, um einen Fall zu vermeiden, der relativ selten auftritt und in den meisten Fällen ziemlich billig ist (die meisten Vergleiche sind). Das wäre ein Nettoverlust.

(Ich habe übrigens gerade mit einer solchen Änderung des Algorithmus experimentiert, und bei der Messung war es tatsächlich ein Effizienzverlust mit üblichen Vergleichen wie dem Standard int comparer).

  

Im zweiten Fall sollte es nie notwendig sein, auf den Vergleich der Q-Eigenschaften (Integer) zurückzugreifen; denn es gibt keine Duplikate (in Ordnungsvergleich) in den P-Werten, daher sollte niemals ein Tie-Break von ThenBy benötigt werden. Trotzdem sehen wir zweimal "Ints: 9 gegen 9". Warum verwenden Sie den ThenBy-Vergleich mit identischen Argumenten?

Wer sagt, dass es keine Duplikate gibt? Der interne Vergleich hat zwei Dinge (nicht unbedingt Referenztypen, so Kurzschlüsse auf Referenzidentität ist nicht immer eine Option) und hat zwei Regeln zu folgen. Die erste Regel benötigte einen Tie-Break, so dass der Tie-Break gemacht wurde.

Der Code ist für Fälle gedacht, in denen für den ersten Vergleich entsprechende Werte vorhanden sein können.

Wenn bekannt ist, dass es für die OrderBy keine gleichwertigen Werte gibt, dann ist es für die Person, die weiß, dass sie keine unnötige ThenBy verwendet, da sie die Person ist, die das möglicherweise wissen kann.

>     
Jon Hanna 29.08.2017 11:45
quelle
-1

Ok, lass uns die Möglichkeiten hier sehen:

  1. T ist ein Werttyp

    Um zu prüfen, ob ein Artikel mit sich selbst verglichen wird, muss zuerst überprüft werden, ob beide Artikel identisch sind. Wie würdest du das tun?

    Sie könnten Equals zuerst und dann CompareTo aufrufen, wenn die Elemente nicht identisch sind. Willst du das wirklich? Die Kosten von Equals werden in etwa gleich sein wie beim Vergleich, also würden Sie tatsächlich die Kosten der Bestellung für genau was verdoppeln? OrderBy vergleicht einfach alle Elemente, Punkt.

  2. T ist ein Referenztyp

    c # lässt Sie nicht nur mit generischen Constraints überladen, so dass Sie Runtime einchecken müssen, wenn T ein Referenztyp ist oder nicht, und rufen Sie dann eine spezifische Implementierung auf, die das oben beschriebene Verhalten ändern würde. Möchten Sie diese Kosten in jedem Fall tragen? Natürlich nicht.

    Wenn der Vergleich teuer ist, implementieren Sie in Ihrer Vergleichslogik eine Referenzoptimierung, um beim Vergleich eines Artikels mit sich selbst keine dummen Kosten zu verursachen, aber diese Wahl muss Ihnen gehören. Ich bin mir ziemlich sicher, dass string.CompareTo genau das tut.

Ich hoffe, dies macht meine Antwort klarer, Entschuldigung für die vorherige kurze Antwort, meine Argumentation war nicht so offensichtlich.

    
InBetween 29.08.2017 07:46
quelle
-1

In einfachen Worten in Fall 1

%Vor%

Wir vergleichen nur die Elemente, die nicht ignoriert werden sollen, wenn das Ergebnis 0 ist. Die Console.WriteLine-Bedingung ist unabhängig von der Ausgabe des Vergleichs. Wenn Sie Ihren Code wie folgt ändern

%Vor%

Ihre Ausgabe ist wie

%Vor%

Gleiches gilt für die zweite Anweisung, hier überprüfen wir die Ausgabe von beiden, also wird für den Vergleich der Zeichenfolge 0 zurückgegeben, dann wird es für den Vergleich gehen, also wird es das eine nehmen und das erforderliche ausgeben. Hoffe es löscht deine Zweifel:)

    
Vivek Shah 29.08.2017 08:46
quelle

Tags und Links