我可以为 TObjectList.IndexOf 传入一个函数,为 TObjectList.Sort 传入另一个函数吗?

发布于 2024-10-21 04:57:59 字数 3158 浏览 8 评论 0原文

总结:

TList.IndexOf(TList 在 Classes.pas 单元中定义)线性迭代所包含的项目,并比较引用。 TList.IndexOf(TList 在 Generics.Collections.pas 单元中定义)也线性迭代所包含的项目,但使用比较器来比较项目是否相等。

TList.Sort 和 TList.Sort 都可以使用比较器。

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

对于在以下单元中定义的 TForceList 类型的实例,我可以使用

instance.Sort(@ForceCompare);

其 Value 字段作为排序标准来对其进行快速排序。但是,当我调用时

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.

Summarization:

TList.IndexOf (TList defined in the unit Classes.pas) iterates linearly through the contained items, and compares the reference. TList.IndexOf (TList defined in the unit Generics.Collections.pas) also iterates linearly through the contained items, but uses a comparer to compare whether the items are equal.

Both TList.Sort and TList.Sort can use a comparer.

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

For an instance of the TForceList type defined in the following unit, I could use

instance.Sort(@ForceCompare);

to QuickSort it using its Value field as sorting criteria. However, when I call

instance.IndexOf(AnotherInstance)

I want to use its ElementZ field as comparing criteria, i.e., the ForceEqual function. I am wondering how can I achieve this?

PS: If the generics collection is used, I guess I could use

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

The unit:

    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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

So尛奶瓶 2024-10-28 04:57:59

非泛型 TObjectList 使用 TList.IndexOf,它只是迭代内部数组并比较指针。

同样,通用 TObjectList 使用 TList.IndexOf,后者使用 IComparerTList.Sort 使用 TArray.Sort 传入在创建列表时分配的任何 IComparer

比较器是私有的,仅在列表构造函数中分配,因此我没有看到覆盖此行为的简单方法。

更新

TList 提供并重载的 Sort,它接受比较器作为参数,无需修改私有比较器。因此,您可以使用一个比较器进行排序,而 indexof 可以使用不同的比较器。

The non-generic TObjectList uses TList.IndexOf, which simply iterates through the internal array and compares pointers.

Likewise, the generic TObjectList<T> uses TList<T>.IndexOf, which uses an IComparer. TList<T>.Sort uses TArray.Sort<T> passing in whatever IComparer was assigned at the list's creation.

The comparer is private and only assigned in the list constructor so I don't see an easy way to override this behavior.

Update

TList<T> provides and overloaded Sort that accepts a comparer as an argument, without modifying the private comparer. So you can sort using one comparer and indexof can use a different one.

心如狂蝶 2024-10-28 04:57:59

Sort 方法背后的整个思想是它真正对列表进行排序...换句话说,在调用排序方法之后,列表中元素的物理顺序会发生更改以满足排序标准。

正如您在自己的 Delphi RTL 代码中所看到的,IndexOf 方法只是通过引用进行线性搜索,返回第一个匹配元素的物理索引。

返回的索引可用于检索列表上的对象,如下所示:

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

您会明白为什么,IndexOf 方法不应返回基于与列表的物理顺序不同的标准的索引...并且如果您首先调用排序,物理顺序反映了传递的排序标准。

也就是说,看起来您可能想要

  • 维护两个不同的列表,按照不同的标准进行排序,并在适当的时候使用一个或另一个。
  • 根据应用程序在给定时间处理的操作的适用标准对列表进行重新排序。

性能更高取决于您的应用程序如何使用这些对象、它正在处理的数据量,甚至是运行时进程可用的内存。

The entire idea behind the Sort method is it really sorts the list... in other words, after calling the sort method, the physical order of the elements on the list is changed to meet the sort criteria.

The IndexOf method, as you can see in your own Delphi RTL code, is just a linear search by reference returning the physical index of the first matching element.

The index returned can be used to retrieve the object on the list, like this:

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

You'll see why, the IndexOf method shall not return a index based on a different criteria than the physical order of the list... and it happens if you call Sort first, the physical order is reflecting the passed sort criteria.

That said, it looks like you may want to

  • maintain two different lists, sorted with different criteria and use one or another when appropriate.
  • re-sort the list based on the applicable criteria for the operation your application is processing at a given time.

What is more performant depends on how your application use those objects, the amount of data it is processing and even the memory available to your process at runtime.

ゝ杯具 2024-10-28 04:57:59

不适用于标准 TObjectList。它(实际上是基本 TList)被编写为支持使用 CustomSort 的自定义排序函数,但对于自定义 IndexOf 没有这样的规定。当然,您可以编写自己的实现方式来实现这种方式。

Not with the standard TObjectList. It (actually the base TList) is written to support a custom sort function using CustomSort, but there's no such provision for a custom IndexOf. Of course, you could write your own implementation of something that works that way.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文