Могу ли я передать одну функцию для TObjectList.IndexOf и другую функцию для TObjectList.Sort? - PullRequest
1 голос
/ 11 марта 2011

Суммирование:

TList.IndexOf (TList, определенный в модуле Classes.pas) итерирует линейно по содержащимся элементам и сравнивает ссылку. TList.IndexOf (TList, определенный в модуле Generics.Collections.pas), также выполняет линейную итерацию по содержащимся элементам, но использует компаратор для сравнения, равны ли элементы.

И TList.Sort, и TList.Sort могут использовать компаратор.

=============================================== ==

Для экземпляра типа TForceList, определенного в следующем блоке, я мог бы использовать

instance.Sort(@ForceCompare);

для быстрой сортировки с использованием поля «Значение» в качестве критерия сортировки. Однако когда я звоню

instance.IndexOf(AnotherInstance)

Я хочу использовать его поле ElementZ для сравнения критериев, то есть функцию ForceEqual. Мне интересно, как я могу этого достичь?

PS: Если используется коллекция дженериков, думаю, я мог бы использовать

TList<TForce>.Create(TComparer<TForce>.Construct(ForceEqual));

Единица измерения:

    unit uChemParserCommonForce;

    interface

    uses
      uMathVector3D,
      Contnrs;

    type

      TForce = class;
      TForceList = class;

      TForce = class
      private
        FElementZ: Integer;
        FValue: TVector3D;
      public
        property ElementZ: Integer read FElementZ;
        property Value: TVector3D read FValue;
        constructor Create(aElementZ: Integer; aX, aY, aZ: Double);
        function ToString(): string; {$IF DEFINED(FPC) OR DEFINED(VER210)} override; {$IFEND}
      end;

      // Mastering Delphi 6 - Chapter 5 -
      TForceList = class(TObjectList)
      protected
        procedure SetObject(Index: Integer; Item: TForce);
        function GetObject(Index: Integer): TForce;
      public
        function Add(Obj: TForce): Integer;
        procedure Insert(Index: Integer; Obj: TForce);
        property Objects[Index: Integer]: TForce read GetObject
          write SetObject; default;
      end;

    function ForceCompare(Item1, Item2: Pointer): Integer;
    function ForceEqual(Item1, Item2: Pointer): Boolean;

    implementation

    uses
      Math, SysUtils;

    function ForceCompare(Item1, Item2: Pointer): Integer;
    begin
      // Ascendent
      //  Result := CompareValue(TForce(Item1).Value.Len, TForce(Item2).Value.Len);
      // Descendent
      Result := CompareValue(TForce(Item2).Value.Len, TForce(Item1).Value.Len);
    end;

    function ForceEqual(Item1, Item2: Pointer): Boolean;
    begin
      Result := TForce(Item1).ElementZ = TForce(Item2).ElementZ;
    end;

    constructor TForce.Create(aElementZ: Integer; aX, aY, aZ: Double);
    begin
      FElementZ := aElementZ;
      FValue := TVector3D.Create(aX, aY, aZ);
    end;

    function TForce.ToString: string;
    begin
      Result := IntToStr(FElementZ) + ' X: ' + FloatToStr(FValue.X) + ' Y: ' +
        FloatToStr(FValue.Y) + ' Z: ' + FloatToStr(FValue.Z);
    end;

    { TForceList }

    function TForceList.Add(Obj: TForce): Integer;
    begin
      Result := inherited Add(Obj);
    end;

    procedure TForceList.SetObject(Index: Integer; Item: TForce);
    begin
      inherited SetItem(Index, Item);
    end;

    function TForceList.GetObject(Index: Integer): TForce;
    begin
      Result := inherited GetItem(Index) as TForce;
    end;

    procedure TForceList.Insert(Index: Integer; Obj: TForce);
    begin
      inherited Insert(Index, Obj);
    end;

    end.

Ответы [ 3 ]

2 голосов
/ 11 марта 2011

Неуниверсальный TObjectList использует TList.IndexOf, который просто перебирает внутренний массив и сравнивает указатели.

Аналогично, универсальный TObjectList<T> использует TList<T>.IndexOf, который использует IComparer,TList<T>.Sort использует TArray.Sort<T>, передавая то, что IComparer было назначено при создании списка.

Сравнитель является закрытым и назначается только в конструкторе списка, поэтому я не вижу простого способа переопределить это поведение,

Обновление

TList<T> предоставляет и перегружает Sort, который принимает компаратор в качестве аргумента, без изменения частного компаратора .Таким образом, вы можете сортировать, используя один компаратор, а indexof может использовать другой.

2 голосов
/ 11 марта 2011

Вся идея метода Sort заключается в том, что он действительно сортирует список ... другими словами, после вызова метода сортировки физический порядок элементов в списке изменяется в соответствии с сортировкой.критерии.

Метод IndexOf, как вы можете видеть в своем собственном коде RTL Delphi, представляет собой просто линейный поиск по ссылке, возвращающей физический индекс первого соответствующего элемента.

Возвращенный индекс можетиспользоваться для извлечения объекта из списка, например:

SomeIndex := AList.IndexOf(SomeObject);
//more code...
//you can re-use the reference...
//and maybe more...
SomeObject := AList[SomeIndex];

Вы поймете, почему метод IndexOf не должен возвращать индекс, основанный на критериях, отличных от физического порядка в списке.... и это произойдет, если вы сначала вызовете сортировку, физический порядок отражает переданные критерии сортировки.

Тем не менее, похоже, что вы можете

  • поддерживать два разных списка,сортировать по разным критериям и использовать тот или иной при необходимости.
  • пересортируйте список на основе применимых критериев для операции, которую ваше приложение обрабатывает в данный момент времени.

Что более эффективно, зависит от того, как ваше приложение использует эти объекты,объем данных, которые он обрабатывает, и даже память, доступная вашему процессу во время выполнения.

1 голос
/ 11 марта 2011

Не со стандартным списком TObjectList.Он (на самом деле базовый TList) написан для поддержки пользовательской функции сортировки с использованием CustomSort, но для пользовательского IndexOf такого положения нет.Конечно, вы можете написать свою собственную реализацию чего-то, что работает таким образом.

...