2012-11-06 10 views
29

Jestem nowicjuszem Delphi i nie wiem, w jaki sposób jest wywoływana metoda sortowania TList of Records w celu sortowania rekordów przez rosnącą wartość całkowitą. Mam zapis jak poniżej:Jak sortować listę ogólną za pomocą niestandardowego porównywalnika?

type 
    TMyRecord = record 
    str1: string; 
    str2: string; 
    intVal: integer; 
    end; 

I rodzajowego listę takich zapisów:

TListMyRecord = TList<TMyRecord>; 

próbowali znaleźć kod-przykład w plikach pomocy i znaleźć ten jeden:

MyList.Sort(@CompareNames); 

Którego nie mogę użyć, ponieważ używa klas. Więc starałem się napisać własną funkcję porównania z nieco różnych parametrów:

function CompareIntVal(i1, i2: TMyRecord): Integer; 
begin 
    Result := i1.intVal - i2.intVal; 
end; 

Ale kompilator zawsze rzuca „nie ma wystarczającej liczby parametrów” - błąd gdy zgłoszę go open.Sort(CompareIntVal);, co wydaje się oczywiste; więc starałem się zatrzymać się bliżej do pliku pomocy:

function SortKB(Item1, Item2: Pointer): Integer; 
begin 
    Result:=PMyRecord(Item1)^.intVal - PMyRecord(Item2)^.intVal; 
end; 

z PMyRecord jak PMyRecord = ^TMyRecord;

Próbowałem różnych sposobów wywoływania funkcji, zawsze się jakiś błąd ...

Odpowiedz

36

przeciążenia Sort powinien być używany jest ten:

procedure Sort(const AComparer: IComparer<TMyRecord>); 

teraz można utworzyć IComparer<TMyRecord> wywołując TComparer<TMyRecord>.Construct . Tak:

var 
    Comparison: TComparison<TMyRecord>; 
.... 
Comparison := 
    function(const Left, Right: TMyRecord): Integer 
    begin 
    Result := Left.intVal-Right.intVal; 
    end; 
List.Sort(TComparer<TMyRecord>.Construct(Comparison)); 

Pisałem funkcję Comparison jako metody anonimowej, ale można też użyć zwykły stary styl funkcji non-OOP, lub metody obiektu.

Jednym z potencjalnych problemów z funkcją porównawczą jest ryzyko przepełnienia liczby całkowitej. Więc możesz zamiast tego użyć domyślnego porównania liczb całkowitych.

Comparison := 
    function(const Left, Right: TMyRecord): Integer 
    begin 
    Result := TComparer<Integer>.Default.Compare(Left.intVal, Right.intVal); 
    end; 

To może być drogie, aby zadzwonić wielokrotnie TComparer<Integer>.Default więc można go schować w zmiennej globalnej:

var 
    IntegerComparer: IComparer<Integer>; 
.... 
initialization 
    IntegerComparer := TComparer<Integer>.Default; 

Inną opcją do rozważenia jest, aby przejść w comparer podczas tworzenia listy. Jeśli tylko sortujesz listę przy użyciu tego zamówienia, jest to wygodniejsze.

List := TList<TMyRecord>.Create(TComparer<TMyRecord>.Construct(Comparison)); 

a następnie można posortować listę

List.Sort; 
+0

Dzięki wybitnie dużo! Czy muszę uwzględnić wszystko w "zastosowaniach" oprócz "zastosowań Generics.Collections, ...", ponieważ dostaję "niezadeklarowany" dla "TComparison" i "IComparer" w 'var Porównanie: TComparison ; IntegerComparer: IComparer ; '? –

+0

Potrzebujesz również Generics.Defaults. Znalazłeś kod źródłowy RTL. To ci pomoże. –

+1

@ David, czy na pewno 'TComparer' jest dobrym wyborem dla podanego kodu? "TComparer" ma być abstrakcyjną klasą podstawową. Proponuję użyć 'TDelegatedComparer' dla twojego kodu. – TLama

2

znalazłem wiele prostsze zmodyfikowaną funkcję sortowania do alphabetize się TList zapisów lub niestandardowej listy elementów.

Przykład

PList = ^TContact; 
    TContact = record    //Record for database of user contact records 
     firstname1 : string[20]; 
     lastname1 : string[20]; 
     phonemobile : Integer;  //Fields in the database for contact info 
     phonehome : Integer; 
     street1 : string; 
     street2 : string; 

type 
    TListSortCompare = function (Item1, 
           Item2: TContact): Integer; 
var 
    Form1: TForm1; 
    Contact : PList;   //declare record database for contacts 
    arecord : TContact; 
    Contacts : TList; //List for the Array of Contacts 

function CompareNames(i1, i2: TContact): Integer; 
begin 
    Result := CompareText(i1.lastname1, i2.lastname1) ; 
end; 

a funkcja zadzwonić do sortowania Listy

Contacts.Sort(@CompareNames); 
+1

Możesz trochę wyczyścić próbkę kodu. Usuń nieużywane zmienne. Dodaj przykład użycia. Popraw składnię. – Kromster

+4

Pierwotne pytanie dotyczyło sortowania listy ogólnej, podczas gdy w tym przykładzie używa się standardowej TList (listy wskaźników), co jest innym scenariuszem. – ByteArts

0

chcę podzielić się moją rozwiązania (w oparciu na wejściu mam zebranych tutaj).

To standardowa konfiguracja. Klasa filedata przechowująca dane pojedynczego pliku w ogólnej klasie TObjectList. Lista ma dwa prywatne atrybuty fCurrentSortedColumn i fCurrentSortAscending w celu kontrolowania kolejności sortowania. Metoda AsString to ścieżka i nazwa pliku połączone.

function TFileList.SortByColumn(aColumn: TSortByColums): boolean; 
var 
    Comparison: TComparison<TFileData>; 
begin 
    result := false; 
    Comparison := nil; 

    case aColumn of 
    sbcUnsorted : ; 
    sbcPathAndName: begin 
         Comparison := function(const Left, Right: TFileData): integer 
            begin 
             Result := TComparer<string>.Default.Compare(Left.AsString,Right.AsString); 
            end; 
        end; 
    sbcSize  : begin 
         Comparison := function(const Left, Right: TFileData): integer 
            begin 
             Result := TComparer<int64>.Default.Compare(Left.Size,Right.Size); 
             if Result = 0 then 
             Result := TComparer<string>.Default.Compare(Left.AsString,Right.AsString); 
            end; 
        end; 
    sbcDate  : begin 
         Comparison := function(const Left, Right: TFileData): integer 
            begin 
             Result := TComparer<TDateTime>.Default.Compare(Left.Date,Right.Date); 
             if Result = 0 then 
             Result := TComparer<string>.Default.Compare(Left.AsString,Right.AsString); 
            end; 
        end; 
    sbcState  : begin 
         Comparison := function(const Left, Right: TFileData): integer 
            begin 
             Result := TComparer<TFileDataTestResults>.Default.Compare(Left.FileDataResult,Right.FileDataResult); 
             if Result = 0 then 
             Result := TComparer<string>.Default.Compare(Left.AsString,Right.AsString); 
            end; 
        end; 
    end; 

    if assigned(Comparison) then 
    begin 
    Sort(TComparer<TFileData>.Construct(Comparison)); 

    // Control the sort order 
    if fCurrentSortedColumn = aColumn then 
     fCurrentSortAscending := not fCurrentSortAscending 
    else begin 
     fCurrentSortedColumn := aColumn; 
     fCurrentSortAscending := true; 
    end; 

    if not fCurrentSortAscending then 
     Reverse; 

    result := true; 
    end; 
end; 
1

Odpowiedź zwięzły:

uses 
    .. System.Generics.Defaults // Contains TComparer 

myList.Sort(
    TComparer<TMyRecord>.Construct(
    function(const Left, Right: TMyRecord): Integer 
    begin 
     Result := Left.intVal - Right.intVal; 
    end 
) 
); 
Powiązane problemy