Главная страница
    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.54 MB
Время: 0.04 c
4-1095408289
gs
2004-09-17 12:04
2004.10.24
Данные системы


11-1081756279
Delphi5.01
2004-04-12 11:51
2004.10.24
KOLSocket &amp; File not found: objects.dcu


8-1091192021
Anf
2004-07-30 16:53
2004.10.24
Информация о видеофайле


3-1096360864
Denizzz
2004-09-28 12:41
2004.10.24
из DBF в Firebird 1.5


1-1097049379
Галинка
2004-10-06 11:56
2004.10.24
Диалоги





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский