Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2004.10.24;
Скачать: CL | DM;

Вниз

Еще по поводу Tlist. Сортировка.   Найти похожие ветки 

 
mRodion ©   (2004-10-08 14:37) [0]

Господа, огромное Вам спасибо за то, что вы есть и читаете иногда даже полный бред нас новичков...

А вопрос у меня такой:
Используя Tlist строю свой список:
http://delphimaster.net/view/1-1097067854/

Хочу реализовать сортировку. Для этого методу Sort списка типа Tlist я должен передать в качестве параметра функцию типа
TListSortCompare = function (Item1, Item2: Pointer): Integer;

Почему-то в качестве параметра нельзя передать такую функцию описанную в самом классе, реализующим список (например, в качестве private метода класса TMyClass в примере по ссылке выше).

Приходится описывать эту функцию вне класса:
TMyClass = class
private
  FMyList:TList
public
  procedure Add (Item:TMyListItem);
  procedure Delete (Index:integer);
  property Items[Index:integer]:TMyListItem read GetItem
end;
function CompareMyItems (Item1, Item2: Pointer): Integer;


Но тогда если пользователь захочет использовать две копии моего списка, то обе копии будут использовать одну и ту же функцию сравнения CompareMyItems?

Если да, то это не удобно, поскольку в каждой копии класса может быть свой тип сортировки (например по разным полям), и результат сравнения элементов для разных списков может быть разным.
Что делать в этом случае? Можно ли как-нибуть передать в качестве параметра функцию CompareMyItems описанную внутри класса?

Надеюсь, ясен вопрос, а то что-то слишком длинный получился...


 
Reindeer Moss Eater ©   (2004-10-08 14:44) [1]

А если я имею экземпляр ТЛист никуда ни в какой класс не входящий?
И я хочу его отсортировать.
Мне что повеситься, или писать за каким-то лешим какой-то ненужный мне класс в котором одним из полей будет мой ТЛист?


 
Суслик ©   (2004-10-08 14:45) [2]

Ты определись, что ты вообще делаешь. Как мне видится ты несколько непоследовательно подходишь. Это выражается в том, что ты говоришь, что

"то обе копии будут использовать одну и ту же функцию сравнения"

и в тоже время

"то это не удобно, поскольку в каждой копии класса может быть свой тип сортировки".

Определись - пользователь использует одну фукнцию, есно с определенной функциональнсотю, или несколько разных?

Если несколько разных, то пусть сам пользователь и передает функцию сравнения - пусть сравнивает как ему хочется. ЭТо забота пользователя твоего класса.

Если все же речь идет об одной функции, то о каком разном типе сортироваке может идти речь? Я так полагаю, что ты имеешь в виду не совсем произвольный тип сортировки, а один из предопределенных? Если да, то организуй несколько функций. И сам переключайся переключайся между ними.


 
KSergey ©   (2004-10-08 14:50) [3]

В нее передать не получится
Но можно в модуле определить соотв. переменную типа

TMyCompProc = procedure (Item1, Item2: Pointer): Integer of object;

implementation
var
 MyCompFunc:TMyCompProc;


Ну и в методе Sort сначала присваивать этой переменной нужный метод класса, а потом вызывать оригинальную Sort.
В CompareMyItems пишем:

function CompareMyItems
begin
  Result := MyCompFunc(Item1, Item2);
end;


Вроде ничего особо не напутал....


 
Reindeer Moss Eater ©   (2004-10-08 15:06) [4]

type
TListSortCompare = function (Item1, Item2: Pointer): Integer;

TMyClass = class
private
 FMyList:TList
 fCompFunc : TListSortCompare;
public
 procedure Add (Item:TMyListItem);
 procedure Delete (Index:integer);
 property Items[Index:integer]:TMyListItem read GetItem
 property SortFunc : TListSortCompare read fCompFunc write fCompFunc;
end;


 
Skier ©   (2004-10-08 15:20) [5]

хм...а если так TMethod(AObjectProc).Code ?


 
Суслик ©   (2004-10-08 15:22) [6]


> хм...а если так TMethod(AObjectProc).Code ?

можно и так, только надо будет первым (кажется) параметром передавать указатель на объект. Можно nil передавать.

Только тогда от клиента, передавшего метод объекта в качестве функции сравнения для tlist, нужно заручится гарантией, что он не будет в этом методе пользоваться self"ом.


 
Суслик ©   (2004-10-08 15:25) [7]


> [5] Skier ©   (08.10.04 15:20)

не, так не выйдет... :)) Поторопился я :)

вызывать метод будет будет tlist. Он же не будет ничего передавать в качестве self. Так, что лажа будет.

ЗЫ. Может я что-то не понял, что было предложено. Поясни тогда свою мысль.


 
mRodion ©   (2004-10-08 16:05) [8]

2Суслик ©   (08.10.04 14:45) [2]:
Моя идея в том, чтобы сделать свой класс списка, тип сортировки в котором задается отдельным свойством. Я хочу вообще оградить пользователя моего списка от задания каких-бы то ни было функций сортировки. Только свойством.
type
 TSortType = (stSortByFilename, stSortByDate, stSortByComment);
 TMyList = class
 private
   FMyList:TList;
   FSortType:TSortType;
   // e.t.c
 public
   procedure Sort;
   // e.t.c
 end;
 

Это для того, чтобы пользователь, используя мой TMyList,  мог сделать следующиее:
var
 List1:TMyList;
 List2:TMyList:
begin
 List1 := TMyList.Create;
 List2 := TMyList.Create;
 //заполнение списков
 List1.SortType := stSortByFilename;
 List1.Sort;
 List2.SortType := stSortByComment;
 List2.Sort;
end;


Если в реализации метода TMyList.Sort я буду вызывать функцию CompareMyItems (см. мой вопрос), которая вынесена за пределы описания класса, то у меня стоит вопрос, как узнать тип сортировки в моем в конкретном экземпляре класса.

Если бы эта функция могла определаться внутри TMyList, то я смог бы получить простой доступ к полю FSortType и провести соответствующее сравнение. Очень похоже на то, что совет Reindeer Moss Eater ©   (08.10.04 15:06) [4] - это то, что мне нужно. Только я не понимаю, как в таком случае нужно определять функцию fCompFunc в разделе implementation.


 
Reindeer Moss Eater ©   (2004-10-08 16:07) [9]

как в таком случае нужно определять функцию fCompFunc в разделе implementation.

Её не обязательно определять там.
Её может определить пользователь твоего класса там где захочет.


 
Reindeer Moss Eater ©   (2004-10-08 16:08) [10]

implementation

....

function SortFunction (...):integer;
begin
.....
end;

end.


 
Суслик ©   (2004-10-08 16:11) [11]


> , как узнать тип сортировки в моем в конкретном экземпляре
> класса.

не нужно узнавать.

Насколько я понимаю из твоего примера у тебя сортировок определенное количество видов. Т.е. у тебя TMyList не просто список, я список для определенных целей. Иначе я пример твоего не понимаю.

Тогда все просто - у тебя есть перечислимый признак SortType, где

procedure TMylist.fSetSortType(value: ...)
begin
   case Value of
     ByName: fSortProc := SortProcByName;
     ByComment: fSortProc := SortPRocByComment;
    ...
  end;
end;


где SortProcByName и SortProcByName под implementation.

в дальнейшем при вызове метода Sort у TList ему передается fSortProc.


 
mRodion ©   (2004-10-08 16:46) [12]

Понял, о чем вы толкуете, только мне нужно немножко не это.
Может быть, я просто других элементарных вещей не понимаю?

Хочу, чтобы функция CompareListItems работала примерно по такому принципу: пользователь задает порядок сортировки у каждого экземпляра класса с помощью одного поля класса (сортировать сначала по одному полю, потом по другому и т.д.). Т.о. создается список из полей сортировки, называемый SortOrderList. Этот список и есть то, что раньше я называл простым словом FSortType.

Соответственно, функция CompareListItems должна знать заданный порядок сортировки. Ее реализацию я вижу примерно такой:
function CompareListItems (Item1, Item2: Pointer): Integer;
var
 pdbitem1, pdbitem2:PTMyItem;
 i:integer;
begin
 pdbitem1 := item1;
 pdbitem2 := item2;
 result := 0;
 i:=0;
 if SortOrderList.Count <= 0 then exit;
 while ((i<SortOrderList.Count) and (result = 0)) do begin
   case SortOrderList.Items[i] of
     udFilename :begin
       if pdbitem1^.FileName < pdbitem2^.FileName then result := -1
       else if pdbitem1^.FileName > pdbitem2^.FileName then result := 1
       else result := 0;
     end;
     udDeviceName:begin
       if pdbitem1^.DeviceName < pdbitem2^.DeviceName then result := -1
       else if pdbitem1^.DeviceName > pdbitem2^.DeviceName then result := 1
       else result := 0;
     end;
     udDateTime : begin
       if pdbitem1^.Time < pdbitem2^.Time then result := -1
       else if pdbitem1^.Time > pdbitem2^.Time then result := 1
       else result := 0;
     end;
     udComment:begin
       if pdbitem1^.Comment < pdbitem2^.Comment then result := -1
       else if pdbitem1^.Comment > pdbitem2^.Comment then result := 1
       else result := 0;
     end;
   end;
   inc (i);
 end;
end;


Так вот у меня в такой конфигурации одна проблема: я не знаю, как мне добраться до поля SortOrderList моего класса TMyList.
Если быть точным, то описание моего класса-списка выглядит примерно таким:
TMyClass = class
private
 FMyList:TList
public
 SortOrderList:TSortOrderList;
 procedure Add (Item:TMyListItem);
 procedure Delete (Index:integer);
 property Items[Index:integer]:TMyListItem read GetItem
end;
function CompareMyItems (Item1, Item2: Pointer): Integer;


Попробую задать вопрос так:
как мне в такой ситуации в реализации функции CompareMyItems добраться до поля SortOrderList моего класса?


 
Суслик ©   (2004-10-08 16:57) [13]


> как мне в такой ситуации в реализации функции CompareMyItems
> добраться до поля SortOrderList моего класса?

через глобальную переменную ПОД implementation.

например так

procedure tmylist.sort();
begin
  WhoStartsSort := self;
  fList.Sort();
end;

Переменную WhoStartsSort использовать в процедуре сравнения.

С учетом того, что у тебя приложение однопоточное усе будет ок.


 
mRodion ©   (2004-10-08 16:58) [14]

2Суслик ©   (08.10.04 16:11) [11]

Ваш пример хорош и красив, но иногда заранее не знаешь, какой порядок сортировки предпочитает пользователь.

Один раз он захочет сортировать сначала по комментариям, потом по датам записей. А потом захочет другую сортировку: по комментариями, а потом по размеру файла, например.

Если есть такие данные:
коммент      дата      размер
ком1         1         12
ком2         3         10
ком2         1         15
ком1         1         6

И пользователь хочет первый тип сортировки, он может с помощью метода QuickSort получить такой список:
коммент      дата      размер
ком1         1         12
ком1         1         6
ком2         1         15
ком2         3         10

При этом видим, что размер не в порядке возрастания. Тогда пользователь может захотеть отсортировать вторым способом. Тогда может получить следующий список:
коммент      дата      размер
ком1         1         6
ком1         1         12
ком2         3         10
ком2         1         15

И когда пользовательских полей не 3, а 30, то я не берусь заранне предсказать все типы сортировок, которые могут понадобиться пользователю. Отсюда и пришла идея разрешить пользователю программы в самом интерфейсе задавать именно порядок сортировки. Почти как в excel"е.


 
Reindeer Moss Eater ©   (2004-10-08 17:00) [15]

Тогда свойством твоего класса надо делать не ссылку на процедуру сортировки, а тип сортировки.
А саму реализацию разных типов рисовать в модуле класса.


 
mRodion ©   (2004-10-08 17:07) [16]

ну так я именно это и пытаюсь сделать.
только не знаю заранее все типы сортировок.


 
Суслик ©   (2004-10-08 17:09) [17]


> только не знаю заранее все типы сортировок.

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

Об этом я тебе сказал с самого начала.

Ты хочешь невозможного - типа я (автор класса) ничего не знаю, но хочу сделать пользователю так здорово, чтобы он ничего не задавал, т.е. по сути тоже ничего не знал, но получал, что хотел. Ерунда какая-то: кто-то должен знать, иначе кирдык.


 
mRodion ©   (2004-10-08 17:11) [18]

2Суслик ©   (08.10.04 16:57) [13]
О! А это идея!
А переменную WhoStartsSort сделать типа TMyList!

Правильно понял?


 
Суслик ©   (2004-10-08 17:13) [19]


> Правильно понял?

точно.


 
mRodion ©   (2004-10-08 17:23) [20]

Спасибо. Похоже в однопоточном случае это поможет.

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


 
Суслик ©   (2004-10-08 17:26) [21]


> использоваться в отдельных потоках...

Когда у это случится у тебя будет столько проблем, что ты про TMyList напрочь забудешь. А когда вспомнишь, твой уровень, очевидно, будет выше (с большинством проблем уже справился), так, что проблем реализовать многопоточный TMyList у тебя не будет.

:)


 
mRodion ©   (2004-10-08 17:33) [22]

А если я обрамлю изменение WhoStartsSort с помощью критических секций, это поможет в многопоточных использованиях данной конструкции?

procedure tmylist.sort();
begin
 SortCriticalSection.Enter;
 WhoStartsSort := self;
 fList.Sort();
 SortCriticalSection.Leave;
end;


 
mRodion ©   (2004-10-08 17:34) [23]

Спасибо за поддержку, но с потоками я уже эксперементирую :)


 
Суслик ©   (2004-10-08 17:36) [24]


> А если я обрамлю изменение WhoStartsSort с помощью критических
> секций, это поможет в многопоточных использованиях данной
> конструкции?

В общем-то да...

Но в ракурсе действительно профессиональной разработки многопоточных приложений, заточенных на производительность, может быть не все так просто и однозначно. Крит. секция, это первое, что приходит в голову. Но бывают намного более тонкие методы взаимодействия потоков.


 
Суслик ©   (2004-10-08 17:38) [25]


> но с потоками я уже эксперементирую :)

с ними не надо экспериментровать.

их надо плотно изучать.

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



Страницы: 1 вся ветка

Текущий архив: 2004.10.24;
Скачать: CL | DM;

Наверх




Память: 0.55 MB
Время: 0.044 c
1-1096497679
Defunct
2004-09-30 02:41
2004.10.24
Exception: Not enough storage is available to process this ..


14-1096877390
gn
2004-10-04 12:09
2004.10.24
я болдею с бенигейтси чесное слово


3-1096208788
eugene32
2004-09-26 18:26
2004.10.24
Вопрос про TDataSet и TQuery


1-1097049347
456
2004-10-06 11:55
2004.10.24
создать кнопку (на форме) во время работы программы


1-1097224161
LKan
2004-10-08 12:29
2004.10.24
Очистка экрана