TList.Sortのヘルプの意味がわかりません

解決


デル太  2006-07-02 02:38:11  No: 22344

いつもお力添えありがとうございます。

Delphi6Proを使っています。
List.Sortを実装したいと思い、Helpを参照しました。

-----
Sort 例
-----
以下の例は、リスト内の各オブジェクトをそれぞれの名前のアルファベット順にソートします。これは、リストがコンポーネント参照のみを格納していることを前提としています。
CompareNames 関数は、リスト内のオブジェクトの比較を行います。ユーザーがボタンをクリックすると、リストがソートされます。

function CompareNames(Item1, Item2: Pointer): Integer;
begin
  Result := CompareText((Item1 as TComponent).Name, (Item2 as TComponent).Name);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  List1.Sort(@CompareText);
end;
-----

呼び出し方の部分は次のようになっています。

  List1.Sort(@CompareText);

これは、CompareText関数を引数として渡しているように思います。
たぶんSortメソッドの中でCompareTextを呼び出していると推察しました。
CompareTextはDelphiが準備した関数ですよね?

そうすると、ユーザが定義している

  function CompareNames

関数はなぜ存在しているのか?とわからなくなってしまいました。

私がSortについて把握しているイメージを説明させていただきます。

  a)List1はItemにTComponent型を保持している
  b)List1.Sort(MyFunc関数のポインタ);  でソートを実行できる
  c)実際はふたつのItemを比較する際にMyFunc(Item1,Item2)が実行される
  d)引数は、Sortメソッドの中で自動的にセットされる(?)、
  e)MyFuncの戻り値でふたつのItemの大小を判断し、
  f)List1の中で並べ替えが行われる

このように考えたので、b)では、自作関数(MyFunc)を引数にする気がしています。
たぶん、何か、根本的に誤解または理解できていない点があるように思います。
また、ご指導いただけると幸いです。


ママん  2006-07-02 03:18:15  No: 22345

Delphi7でも直ってないです。
×List1.Sort(@CompareText);
○List1.Sort(@CompareNames);


デル太  2006-07-02 03:42:00  No: 22346

ママんさん、ありがとうございます。
ヘルプの誤りだったんですね。おかげさまで、

  List.Sort(@TForm1.CompareNames);

の形で自作関数を呼び出すことができました。

※CompareNamesをTForm1のメンバ関数として実装したので、頭にTForm1が必要みたいですね。

これでソートを実装できるととても便利だと思います。ありがとうございます。

-----

ところが、今度は自作関数の中でエラーが発生しています。
もう少しお力添えくださいませ。

ヘルプの例で言うと、Itemの型指定部分を次のように変更して実装しました。

function CompareNames(Item1, Item2: Pointer): Integer;
begin
  Result := CompareText(TMyClass(Item1).Name, TMyClass(Item2).Name);・・(A)
end;

このコードが呼び出された段階で、次の状況になってしまいます。

  ・Item1.Nameは取得できる
  ・Item1.Nameが取得できない  ←読み込み違反のエラーになる

値を調べると

  ・Item1=$ED77E0
  ・Item2=$9

とふたつの引数で値に違いが見つかりました。

この読込違反が起きるのは、Sortの使い方が間違っているからなのでしょうか?
Item1とItem2は、自動的に渡されているので原因が想像できず困っています。

また、もうひとつわからない点が出ています。
上記(A)で、

  TMyClass(Item1).Name

の形で型を指定しています。
ヘルプの例と異なっている理由は、

  (Item1 as TComponent).Name

の場合に次のメッセージが出てコンパイルできないためです。

  ・この型には演算子は使えません

何かこの辺が原因を示唆しているのでしょうか・・?


デル太  2006-07-02 05:13:19  No: 22347

もしかするとTList(実際はTObjectList)でのコンポーネントの持ち方に問題があるのかも?と気づきました。
そこで、ヘルプの記述に従った形で、典型的なコードを実行したところ期待通りソートされました。

TObjectListのItemにnilを代入したら、上記と同様のエラーが発生します。
TList.Sortと比較関数の問題ではないと言えそうですね。

この質問は解決として、自分でコードを追求してみます。

ママんさん、みなさん、ありがとうございました。

以下のコードはフォームに

  ・Button1
  ・Memo1

を貼り付けて実行しました。
TObjectListを使うため、UsesにContnrsを追加しました。

-----
function CompareNames(Item1, Item2: Pointer): Integer;
begin
  Result := CompareText(TComponent(Item1).Name, TComponent(Item2).Name);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  AObjList: TObjectList;
  AComponent: TComponent;
  i: Integer;
begin
  AObjList := TObjectList.Create;

  AComponent := TComponent.Create(Self);
  AComponent.Name := 'aaaaa';
  AObjList.Add(AComponent);
  AComponent := TComponent.Create(Self);
  AComponent.Name := 'ccccc';
  AObjList.Add(AComponent);
  AComponent := TComponent.Create(Self);
  AComponent.Name := 'bbbbb';
  AObjList.Add(AComponent);

  for i := 0 to AObjList.Count -1 do
  begin
    Memo1.Lines.Add(TComponent(AObjList.Items[i]).Name);
  end;

  //AObjList.Items[1] := nil;

  AObjList.Sort(@CompareNames);

  for i := 0 to AObjList.Count -1 do
  begin
    Memo1.Lines.Add(TComponent(AObjList.Items[i]).Name); //ソートされて追加される
  end;

  AObjList.Clear;
end;


ママん  2006-07-02 10:24:53  No: 22348

寝る前にサンプルを投下
これで基本概念を理解してください。
明日は深夜までここへは来れません

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Contnrs;

type

  PMyItem = ^TMyItem;
  TMyItem = record
    myName:String;  //自分で定義(本来被りそうな名前は使用するべきでない)
  end;

  TMyObject = class(TObject)
  private
  public
    myName:String;  //自分で定義(本来被りそうな名前は使用するべきでない)
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Button2: TButton;
    Button3: TButton;
    Memo1: TMemo;
    Button4: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
  private
    { Private 宣言 }
    AObjList: TObjectList;
    AList   : TList;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function CompareMyItemName(Item1, Item2: Pointer): Integer;
begin
  Result := CompareText(PMyItem(Item1).myName, PMyItem(Item2).myName);
end;

function CompareMyObjectName(Item1, Item2: Pointer): Integer;
begin
  Result := CompareText(TMyObject(Item1).myName, TMyObject(Item2).myName);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  myObj1:TMyObject;
  myItem:PMyItem;
begin
//追加の例
  new(myItem);
  myItem.myName:=Edit1.Text;
  AList.Add(myItem);

  myObj1:=TMyObject.Create;
  myObj1.myName:=Edit1.Text;
  AObjList.Add(myObj1);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  AObjList:= TObjectList.Create;
  AList   := TList.Create;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
//ソートの例
  AObjList.Sort(@CompareMyObjectName);
  AList.Sort(@CompareMyItemName);
end;

procedure TForm1.Button3Click(Sender: TObject);
var i:integer;
begin
//列挙の例
  Memo1.Clear;
  Memo1.Lines.Add('--AList--');
  for i:=0 to AList.Count-1 do
    Memo1.Lines.Add(PMyItem(AList.Items[i]).myName);

  Memo1.Lines.Add('');
  Memo1.Lines.Add('--AObjList--');
  for i:=0 to AObjList.Count-1 do
    Memo1.Lines.Add(TMyObject(AObjList.Items[i]).myName);

end;

procedure TForm1.Button4Click(Sender: TObject);
var i:integer;
begin
//削除の例
  for i:=AList.Count-1 downto 0 do
    Dispose(PMyItem(AList.Items[i]));
  AList.Clear;

  for i:=AObjList.Count-1 downto 0 do
    TMyObject(AObjList.Items[i]).Free;
  AObjList.Clear;
end;

end.


Fusa  2006-07-02 23:22:29  No: 22349

> TObjectListのItemにnilを代入したら、上記と同様のエラーが発生します。
> TList.Sortと比較関数の問題ではないと言えそうですね。

CompareTextの仕様を忘れましたが
単にnilチェックする必要があるだけでは?

function CompareNames(Item1, Item2: Pointer): Integer;
begin
  if (not Assinged(Item1)) and (not Assinged(Item2)) then
  begin
    Result := 0;
  end else
  if (not Assinged(Item1)) and (Assinged(Item2)) then
  begin
    Result := -1;
  end else
  if (Assinged(Item1)) and (not Assinged(Item2)) then
  begin
    Result := +1;
  end else
  begin
    Result := CompareText(TComponent(Item1).Name, TComponent(Item2).Name);
  end;
end;

こんなんでしたっけ?

比較関数は、リストに
    ItemA
    ItemB
    ItemC
    ItemD
    ItemE
が入っている場合に、List.Sortが呼び出されると
CompareNames(ItemA, ItemB)が呼び出されて
結果が正か負か同じかでItemAとItemBの場所を入れ替えるかどうか決める

同様に、
    CompareNames(ItemB, ItemC)
    CompareNames(ItemC, ItemD)
を呼び出していくという比較が行われて、その結果で順番がそろう
という概念ですよ。
(概念だけで、実際は上から順番に比較するのではないと思いますけど。)

TComponent(Item1).Name  と書いているのですから
Item1にnilが入ってきたら、nilの対応しないと誤動作するでしょう。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加