Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум «Всё о Паскале» _ Free Pascal, Pascal ABC и другие _ TList

Автор: compiler 15.07.2007 0:22

Добрый день
Существует ли список в FP, как встроенный тип?
говорят что да но найти о нем в руководстве не могу ни слова sad.gif

Автор: volvo 15.07.2007 0:28

Не совсем "встроенный". Описан в модуле Classes... А информация о нем - в файле ref.pdf: "Chapter 2.46 TList"

Также может быть полезно посмотреть про TFPList (в разделе 2.42 того же файла), поскольку

Цитата
Contrary to TList, TFPList has no notification mechanism. If no notification mechanism is used, it is better to use TFPList instead of TList, as the performance of TFPList is much higher.

Автор: compiler 15.07.2007 0:32

спасибо...
а что такое notification mechanism?


Добавлено через 5 мин.
а всетаки в документации найти не могу... у меняв Reference guide for Free Pascal, version 2.0.4 Document version 2.0тут Chapter 2 это про Сonstants...

Автор: volvo 15.07.2007 0:52

Документация утверждает ,что у TList-а есть механизм, позволяющий сообщать об изменениях, произошедших в списке, что может замедлить общее быстродействие. Правда, где бы не встречалось упоминание этого самого "notification mechanism", везде говорится в таком стиле: "не знаю, что это такое, никогда не пользовался этим, однако оно есть".

Я вот тоже не пользовался (а может, и пользовался, только не знаю, что это так называется) - просто предупреждаю, о том, о чем написано в доках.

Добавлено через 1 мин.
P.S.
Сорри, это не ref.pdf, а RTL.pdf, естественно...

Автор: compiler 15.07.2007 22:08

мда... ихний список совсем не список... мне совсем непонятно как им пользоваться, смотрел дукументацию ( где нет ни одного примера ), исходники, был на оф. сайте, все равно не понял... может у кого есть простенький пример показывающий его использывывание?

Автор: volvo 15.07.2007 22:37

А может лучше ты расскажешь, что хочешь получить? А то знаешь ли, примеры могут быть разные, вдруг тебе СОВСЕМ не это надо? Чего хранить-то хочешь, вообще? Сохранил, что потом делать с данными?

Автор: compiler 15.07.2007 22:48

Цитата(volvo @ 15.07.2007 18:37) *
А может лучше ты расскажешь, что хочешь получить? А то знаешь ли, примеры могут быть разные, вдруг тебе СОВСЕМ не это надо? Чего хранить-то хочешь, вообще? Сохранил, что потом делать с данными?

в каждой ячейке списка у меня должно быть указатель на следующий элемент либо nil, если это последний элемент, пользовательский тип и, возможно, переменная -- указывывающая порядковый номер ячейки. Список должен , методами добавления/удаления новых ячеек с обоих сторон списка(обладать конструктором/деструктором для отдельных ячеек), методами доступа к полям любой из ячеек , может еще что-то. Все должно иметь возможность выполняться имея лишь указатель на ячейку.

Автор: volvo 15.07.2007 23:58

Цитата
в каждой ячейке списка у меня должно быть указатель на следующий элемент либо nil, если это последний элемент
Все, про TList можешь забыть - реализуй список сам...

Пойми: вместо того, чтобы как в каменном веке реализовывать список с нуля, тебе предлагают уже готовый контейнер для хранения указателей (в TVision это называется "коллекция"), позволяющий тебе искать элементы, сортировать, производить над каждым из них (или над теми, которые удовлетворяют какому-либо условию, это уже как реализуешь) какие-то действия... А ты вместо этого хочешь хранить "указатель на следующий элемент", и делать все вручную? Тогда делай вручную...

Автор: compiler 16.07.2007 0:09

Цитата(volvo @ 15.07.2007 19:58) *

Все, про TList можешь забыть - реализуй список сам...

Пойми: вместо того, чтобы как в каменном веке реализовывать список с нуля, тебе предлагают уже готовый контейнер для хранения указателей (в TVision это называется "коллекция"), позволяющий тебе искать элементы, сортировать, производить над каждым из них (или над теми, которые удовлетворяют какому-либо условию, это уже как реализуешь) какие-то действия... А ты вместо этого хочешь хранить "указатель на следующий элемент", и делать все вручную? Тогда делай вручную...

просто как работать с классическим списком я читал, а как с таким даже не представляю... указатель мне нужен просто что б как-то обращаться именно к этому элементу...

Автор: hardcase 17.07.2007 5:06

Цитата(compiler @ 15.07.2007 21:09) *

просто как работать с классическим списком я читал, а как с таким даже не представляю... указатель мне нужен просто что б как-то обращаться именно к этому элементу...

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

Автор: compiler 17.07.2007 17:12

А простым примером по использовыванию никто не поделиться?

Автор: volvo 17.07.2007 18:08

Так, например:

uses classes;

type
PT = ^T;
T = record
X, Y: integer;
end;

function compare(a, b: pointer): integer;
begin
if PT(a)^.X > PT(b)^.X then compare := 1
else
if PT(a)^.X < PT(b)^.X then compare := -1
else compare := PT(a)^.Y - PT(b)^.Y;
end;
procedure print(p: pointer);
begin
with PT(p)^ do begin
writeLn('X = ', X:2, ' Y = ', Y:2);
end;
end;
procedure incr(p: pointer);
begin
with PT(p)^ do begin
inc(X); inc(Y);
end;
end;

var
p: PT;
my_list: TFPList;
i: integer;

begin
my_list := TFPList.Create();
for i := 1 to 10 do begin
new(p);
with(p^) do begin
X := random(15); y := random(15);
writeLn('X = ', X:2, ' Y = ', Y:2);
end;
my_list.add(p);
end;

writeln('for each');
my_list.foreachcall(@incr, nil);
my_list.foreachcall(@print, nil);
writeln('sorting');
my_list.sort(@compare);
my_list.foreachcall(@print, nil);

// здесь удалить все элементы и сам список

end.


Автор: compiler 17.07.2007 18:25

volvo, а в каком режиме ты компилировал?
мой лог

Код
ex.pas(47,28) Error: Incompatible type for arg no. 1: Got "<address of procedure(Pointer);Register>", expected "<procedure variable type of procedure(Pointer, Pointer);Register>"
ex.pas(48,29) Error: Incompatible type for arg no. 1: Got "<address of procedure(Pointer);Register>", expected "<procedure variable type of procedure(Pointer, Pointer);Register>"
ex.pas(51,29) Error: Incompatible type for arg no. 1: Got "<address of procedure(Pointer);Register>", expected "<procedure variable type of procedure(Pointer, Pointer);Register>"
ex.pas(55,4) Fatal: There were 3 errors compiling module, stopping
ex.pas(55,4) Fatal: Compilation aborted

если использовать {$mode objfpc}, то на одну ошибку меньше smile.gif

Автор: volvo 17.07.2007 18:56

У меня по умолчанию выставлен {$mode delphi}

Автор: compiler 17.07.2007 19:05

Цитата(volvo @ 17.07.2007 14:56) *
У меня по умолчанию выставлен {$mode delphi}
[оффтоп]а у меня диалект...[/оффтоп] спасибо, так все компилируется, теперь будем разбираться...

Автор: volvo 17.07.2007 19:25

Для диалекта - надо добавить кое-что в заголовки процедур:

{$mode objfpc}
uses classes;

type
PT = ^T;
T = record
X, Y: integer;
end;

function compare(a, b: pointer): integer;
begin
if PT(a)^.X > PT(b)^.X then compare := 1
else
if PT(a)^.X < PT(b)^.X then compare := -1
else compare := PT(a)^.Y - PT(b)^.Y;
end;
procedure print(p: pointer; arg: pointer); // Еще один параметр
begin
with PT(p)^ do begin
writeLn('X = ', X:2, ' Y = ', Y:2);
end;
end;
procedure incr(p: pointer; arg: pointer); // И здесь тоже ...
begin
with PT(p)^ do begin
inc(X); inc(Y);
end;
end;

var
p: PT;
my_list: TFPList;
i: integer;

begin
my_list := TFPList.Create();
for i := 1 to 10 do begin
new(p);
with(p^) do begin
X := random(15); y := random(15);
writeLn('X = ', X:2, ' Y = ', Y:2);
end;
my_list.add(p);
end;

writeln('for each');
my_list.foreachcall(@incr, nil);
my_list.foreachcall(@print, nil);
writeln('sorting');
my_list.sort(@compare);
my_list.foreachcall(@print, nil);

end.


Автор: compiler 17.07.2007 19:39

Цитата(volvo @ 17.07.2007 15:25) *
Для диалекта - надо добавить кое-что в заголовки процедур
я все равно не могу понять способ вызова процедур...
на примере incr
my_list.foreachcall(@incr, nil);//для каждого элемента my_list вызываем эту процедуру
зачем передавать nil?
procedure incr(p: pointer; arg: pointer); 
//получаем 2 аргумента первый указатель на процедуру,
// второй -- не ясно что...
begin
with PT(p)^ do begin // PT(p)^ ? р-- это ж ведь указатель на процедуру, а не на ячейку или список...
inc(X); inc(Y);
end;
end;



Автор: volvo 17.07.2007 19:50

С пониманием природы первого указателя у тебя проблема...

Цитата
получаем 2 аргумента первый указатель на процедуру, второй -- не ясно что...
С чего бы это? Смотри, что написано:

Цитата(rtl.pdf)
2.42.21 TFPList.ForEachCall
Synopsis: Call a procedure or method for each pointer in the list.
Declaration: procedure ForEachCall(proc2call: TListCallback;arg: pointer)
procedure ForEachCall(proc2call: TListStaticCallback;arg: pointer)
Visibility: public
Description: ForEachCall iterates over all pointers in the list and calls proc2call, passing it the pointer and the additional arg data pointer. Proc2Call can be a method or a static procedure.

То есть, первый аргумент - это сам указатель, хранящийся в списке (над элементом, на который он указывает, надо произвести какие-то действия), а второй - дополнительный указатель.

Второй ЗДЕСЬ не используется.

Автор: compiler 17.07.2007 20:45

Цитата(volvo @ 17.07.2007 15:50) *
С чего бы это?
туманно все как-то...

посмотрев исходники кажеться понял зачем nil -- просто так..

my_list.foreachcall(@incr, nil);
//вызов процедуры

procedure incr(p: pointer; arg: pointer); // И здесь тоже ...
//реализация процедуры
begin
with PT(p)^ do begin
inc(X); inc(Y);
end;
end;


procedure TFPList.ForEachCall(proc2call:TListCallback;arg:pointer);
//реализация метода(исходники)
var
i : integer;
p : pointer;
begin
For I:=0 To Count-1 Do
begin
p:=FList^[i];
if assigned(p) then
proc2call(p,arg);
end;
end;

type TListCallback = procedure(
//реализация типа (документация)
data: pointer; //Data pointer from the list.
arg: pointer // Parameter passed to the ForEachCall call. /не нужный параметр?
) of object;

итак мы вызываем метод(в качестве первого параметра используем указатель(почему?) на процедуруа второй оставляем пустым), процедура возвразщает 2 указателя, которые преобразуются в некий TListCallback. Тоесть мы для вызова ForEachCall используем два лишних указателя .Так?

Автор: hardcase 17.07.2007 21:07

TListCallback - это процедура которую будет вызывать ForEachCall для КАЖДОГО элемента списка и передавать ей этот элемент первым параметром.
Втрой указаталь - arg нужен для того, чтобы передавать в TListCallback некие дополнительные параметры, например, если мы хотим отфильтровать список, в качестве arg будет выстпуать новый список, в котором будут аккумулироваться разультаты фильтрации.

Автор: volvo 17.07.2007 21:12

Во-первых, в моем примере используется не TListCallback, а TListStaticCallback, ведь у меня вызываемая процедура не является методом класса. Хотя это не важно, есть 2 версии ForEachCall (для метода класса и для статической процедуры) ...

Цитата
в качестве первого параметра используем указатель <...> на процедуру
Это не указатель на процедуру, а адрес процедуры. Потому что так реализовано.
Цитата
а второй оставляем пустым
Хочешь, я приведу тебе пример, когда тебе понадобится использовать и второй указатель (а не оставлять его пустым)? Ясно же написано: дополнительные данные. Вот попробуй, например, сделать, чтобы при первом вызове процедуры Incr координата X каждой записи, что в списке увеличивалась на 4, а Y - на 8, а при втором вызове - X уменьшалось бы на 3, а Y - увеличивалось на 5. С использованием второго параметра - элементарно (не привлекая глобальных переменных, ибо они - зло)...

Цитата
Тоесть мы для вызова ForEachCall используем два лишних указателя
Если ты считаешь заложенную в реализации гибкость "лишней" - тебя ж никто не заставляет использовать это. Напиши свое, естественно, ты сделаешь это гораздо лучше, правда?

Автор: compiler 17.07.2007 21:15

хорошо, но все же почему мы вызываем(@incr, nil), а не (incr, nil)?

Автор: compiler 18.07.2007 22:45

вопрос по добавлению нового элемент есть метод Add.

Цитата
public function TFPList.Add(Item: Pointer):Integer;
Description
Add adds a new pointer to the list after the last pointer (i.e. at position Count, thus increasing the item count with 1. If the list is at full capacity, the capacity of the list is expanded, using the Grow method.

но как что ему передавать не что он возвращает не указано... так что это за параметры?

Автор: volvo 18.07.2007 23:10

Цитата
так что это за параметры?
На входе - тот самый указатель (на данные), который ты добавляешь в список. На выходе - индекс элемента списка, в который этот указатель был занесен.

Добавлено через 2 мин.
Цитата
почему мы вызываем(@incr, nil), а не (incr, nil)
Потому, что при вызове (Incr, nil) компилятор может посчитать, что ты хочешь передать результат вычисления функции, а не саму функцию как параметр, следовательно, его надо переубедить... А как? Правильно, добавляем @, и двусмысленности не возникает.

Автор: compiler 18.07.2007 23:26

хорошо, вроде разобрались...
спасибо.



Автор: compiler 20.07.2007 18:49

есть задача а не получается smile.gif
существует некий пользовательский класс С, он состоит из нескольких полей, одним из которых является List: TFPList; STATIC;. используя конструктор мы добавляем туда ячейки

{private class} procedure C.Metod2(const Node:TNode);
var
p: ^TNode;
begin
new(p);
p^:=Node;
self.List.Add(p);
end;

, где
TNode = record      
U:UserTipe;;
end;

теперь деструктор
{private class} procedure C.Metod2();
var
SomeU:UserTipe;
begin
SomeU:=self.List[0].TNode.U;//сохраняем поле обьекта <---ОШИБКА!!!
MySnake(MyList[0]).Free; //освобождаем объект
MyList.Delete(0); //удаляем ссылку на него
end;
получаю набор ошибок... в модуле использую {$STATIC ON} и {$mode objfpc}.

что делать?
заранее благодарен.

Автор: volvo 20.07.2007 18:54

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

Автор: compiler 20.07.2007 19:10

***



Прикрепленные файлы
Прикрепленный файл  u4volvo.pas ( 623 байт ) Кол-во скачиваний: 317
Прикрепленный файл  UT.pas ( 125 байт ) Кол-во скачиваний: 328

Автор: volvo 20.07.2007 19:26

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

  SomeU := PUser(List[0])^;
, где PUser = ^UserTipe;

А что дальше у тебя происходит, я не знаю, MySnake не определено...

Автор: compiler 20.07.2007 19:31

Цитата(volvo @ 20.07.2007 15:26) *
Ну, допустим, для того, чтобы сохранить значение нулевого элемента списка, тебе достаточно сделать:
  SomeU := PUser(List[0])^;
, где PUser = ^UserTipe;
спасибо
Цитата(volvo @ 20.07.2007 15:26) *
А что дальше у тебя происходит, я не знаю, MySnake не определено...
MySnake читается как MyC smile.gif

Добавлено через 14 мин.
может я что-то делаю не так, но получаю
Цитата
u4volvo.pas(31,16) Warning: Local variable "PUser" does not seem to be initialized
u4volvo.pas(31,11) Error: Incompatible types: got "^UserTipe" expected "UserTipe"
u4volvo.pas(31,16) Fatal: Syntax error, ";" expected but "(" found
u4volvo.pas(31,16) Fatal: Compilation aborted



Прикрепленные файлы
Прикрепленный файл  u4volvo.pas ( 635 байт ) Кол-во скачиваний: 349

Автор: volvo 20.07.2007 20:25

Я же написал:

Цитата
где PUser = ^UserTipe;
, а значит, я имел в виду описание типа PUser, как указателя на UserTipe, но никак не переменной такого типа...

Автор: compiler 20.07.2007 20:34

Цитата(volvo @ 20.07.2007 16:25) *
Я же написал , а значит, я имел в виду описание типа PUser, как указателя на UserTipe, но никак не переменной такого типа...
от этого у меня результат не меняется, только в Incompatible types: got "^UserTipe" expected "UserTipe" вместо "^UserTipe" есть "PUser"...


Прикрепленные файлы
Прикрепленный файл  u4volvo.pas ( 609 байт ) Кол-во скачиваний: 313

Автор: volvo 20.07.2007 20:39

Да нельзя делать это с переменной! Нужно приводить к типу:

procedure MyC.Metod2();
type
PUser = ^UserTipe; // <-- Хотя это желательно сделать при описании типа UserTipe, в том же модуле
var
SomeU: UserTipe;
begin
SomeU := PUser(List[0])^;
...

Автор: compiler 20.07.2007 21:55

спасибо теперь эта часть( smile.gif ) работает...

Цитата(volvo @ 20.07.2007 16:39) *
Хотя это желательно сделать при описании типа UserTipe, в том же модуле
Почему?
и еще
{...}implementation{...}
procedure MyC.Metod2();
var
SomeU:UserTipe;
begin
SomeU := PUser(List[0])^; //тут, благодаря тебе, работает..
self.List[0].Free; //а тут нет sad.gif
//необходимо отчищать память где находилось то на что указывал указатель, но как?
self.List.Delete(0);
end;
end.



Автор: volvo 20.07.2007 22:08

Вот что мне непонятно - это твое нежелание использовать ООП везде, а не только где-то... Смотри:

ut.pas

type
PUser = ^UserTipe;
UserTipe = object
x: integer;

destructor done;
end;

implementation

destructor UserTipe.Done;
begin
// Пока можно оставить пустым
end;


Второй файл

...
procedure MyC.Metod2();
var
SomeU: UserTipe;
begin
SomeU := PUser(List[0])^; // Сохраняем данные
Dispose(PUser(List[0]), Done); // Удаляем объект по указателю ...
List.Delete(0); // ... и сам указатель
end;
...

Не нужно Self все время за собой таскать, компилятор и так разберется...

Цитата
Почему?
Именно потому, что типы объекта и указателя на него логически связаны сильнее, чем тип списка и указателя на объект... Да и еще. Мало ли, где тебе придется использовать указатель на объект, что будешь делать - определять несколько раз один и тот же тип указателя?

Автор: compiler 20.07.2007 22:27

одни вопросы smile.gif

Цитата(volvo @ 20.07.2007 18:08) *
Вот что мне непонятно - это твое нежелание использовать ООП везде, а не только где-то...
Так получается smile.gif
Цитата(volvo @ 20.07.2007 18:08) *
Пока можно оставить пустым
Пока? А что потм туда надо будет писать?
Цитата(volvo @ 20.07.2007 18:08) *
Dispose(PUser(List[0]), Done);
а что будет работать быстрее данный способ или c FreeMem и определением размера(хотя его можна задать константой)?
Цитата(volvo @ 20.07.2007 18:08) *
Не нужно Self все время за собой таскать, компилятор и так разберется...
мне так удобней...
Цитата(volvo @ 20.07.2007 18:08) *
Именно потому, что типы объекта и указателя на него логически связаны сильнее, чем тип списка и указателя на объект... Да и еще. Мало ли, где тебе придется использовать указатель на объект, что будешь делать - определять несколько раз один и тот же тип указателя?
убедил smile.gif

Автор: volvo 20.07.2007 23:45

Цитата
мне так удобней...
Мало ли, как тебе удобнее... Ты мало того, что делаешь лишние действия, так еще и рискуешь нарваться на проблемы. Не забывай, что ты работаешь с Class Methods, так что Self может указывать на Object Instance, а может и на VMT...

Кстати, то что я написал не сработает. Вылетит программа с RTE 210... Вот так - работает:
procedure MyC.Metod2();
var
SomeU: UserTipe;
L: PUser;
begin
SomeU := PUser(List[0])^;
writeln(someu.X);

L := List[0];
Dispose(L); // Это удалит все, что надо - проверено с использованием heaptrc

List.Delete(0);
end;


Автор: compiler 20.07.2007 23:56

Цитата(volvo @ 20.07.2007 19:45) *
Не забывай, что ты работаешь с Class Methods, так что Self может указывать на Object Instance, а может и на VMT...
кто такой VMT?
Цитата(volvo @ 20.07.2007 19:45) *
heaptrc
а я с этими дебаргерами так и не разобрался...

Автор: hardcase 25.07.2007 19:30

Цитата(compiler @ 20.07.2007 20:56) *
кто такой VMT?

Virtual methods table - таблица методов класса. Фактически является "объектом класса".