Самый эффективный способ удалить элементы в массиве

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Сообщение ZW » 09.09.2006 14:18:05

jwv писал(а):
закинь сюда описание типа.


Control = record
speed:integer;
end;

DS1 = record
path:array of integer;
flagp:byte;
temp:array of integer;
etemp:array of Control;
end;
xarray = array of DS1;//это для процедуры удаления
DS2 = record
x:array of DS1;
flagx:byte;
Y:integer;
end;
DS3 = record
D:array of DS2;
flags:byte;
end;

var

DS:DS2;

надо выкидывать элементы из DS.d[i].x[j]
на поля flag* можно не обращать внимания, они не понадобились, скоро выкину
ZW
новенький
 
Сообщения: 26
Зарегистрирован: 16.08.2006 15:11:23

Сообщение debi12345 » 09.09.2006 20:38:01

ZW писал(а):

закинь сюда описание типа.


Control = record
speed:integer;
end;


надо выкидывать элементы из DS.d[i].x[j]
на поля flag* можно не обращать внимания, они не понадобились, скоро выкину

Перестаньте стучаться в закрытую дверь - используйте связанные списки, типа :

rec1 = record
value: integer;
.. ( your data structures )
prev: ^rec1;
nex: ^rec1;
end;

Тогда удаление будет заключаться в освобождении памяти и корректировке "prev" & "next" соседних елементов - всего 3 команды.

Вообще, есть куча типов, реализующих этот принцип - начиная с TList, они спасут от возни с динамической памятью.
Лично я рекомендую контейнерные типы и методы из библиотеки MSEgui - там есть все, что душе угодно.
Аватара пользователя
debi12345
долгожитель
 
Сообщения: 5761
Зарегистрирован: 10.05.2006 23:41:15
Откуда: Ташкент (Узбекистан)

Сообщение ZW » 09.09.2006 21:28:34

debi12345 писал(а):Перестаньте стучаться в закрытую дверь - используйте связанные списки, типа :

rec1 = record
value: integer;
.. ( your data structures )
prev: ^rec1;
nex: ^rec1;
end;

Тогда удаление будет заключаться в освобождении памяти и корректировке "prev" & "next" соседних елементов - всего 3 команды.

Вообще, есть куча типов, реализующих этот принцип - начиная с TList, они спасут от возни с динамической памятью.
Лично я рекомендую контейнерные типы и методы из библиотеки MSEgui - там есть все, что душе угодно.


Мне бы пример посмотерть...
ZW
новенький
 
Сообщения: 26
Зарегистрирован: 16.08.2006 15:11:23

Сообщение debi12345 » 10.09.2006 16:57:38

Вот что сам Мартин (автор MSEgui ) пишет :

==============
To delete an item of a dynamic array of any type do:
"
function dynarrayelesize(const typinfo: pdynarraytypeinfo): integer;
var
ti: pdynarraytypeinfo;
begin
ti:= typinfo;
{$ifdef FPC}
inc(pchar(ti),ord(ti^.namelen));
result:= ti^.elesize;
{$else}
inc(pchar(ti),length(ti^.name));
result:= ti^.elsize;
{$endif}
end;

type
integerarty = array of integer;
yourrecty = record
a: integer;
b: string;
c: integerarty;
end;
yourrecarty = array of yourrecty;

procedure deleteitem(var value: yourrecarty; const aindex: integer);
var
int1: integer;
begin
if (aindex < 0) or (aindex > high(value)) then begin
tlist.Error(SListIndexError, aindex);
end;
if high(value) = 0 then begin
value:= nil;
end
else begin
finalize(value[aindex]);
int1:= dynarrayelesize(pdynarraytypeinfo(typeinfo(yourrecarty)));
move((pchar(value)+int1*(aindex+1))^,(pchar(value)+int1*aindex)^,
int1*(high(value)-aindex));
fillchar(value[high(value)],sizeof(value[0]),0);
//prevent finalize
setlength(value,high(value));
end;
end;
"

Optimized version for one dimensional arrays:
"
procedure deleteitem(var value: yourrecarty; const aindex: integer);
//value = one dimensional array of type
const
dynarrayrecsize = sizeof(tdynarrayindex) + sizeof(ptrint);
var
int1: integer;
po1: ppointer;
begin
if (aindex < 0) or (aindex > high(value)) then begin
tlist.Error(SListIndexError, aindex);
end;
if high(value) = 0 then begin
value:= nil;
end
else begin
finalize(value[aindex]);
int1:= dynarrayelesize(pdynarraytypeinfo(typeinfo(yourrecarty)));
move((pchar(value)+int1*(aindex+1))^,(pchar(value)+int1*aindex)^,
int1*(high(value)-aindex));
po1:= pointer(value) - dynarrayrecsize;
reallocmem(po1,high(value)*int1+dynarrayrecsize);
pointer(value):= pointer(po1) + dynarrayrecsize;
dec(pdynarrayindex(pointer(value)-sizeof(tdynarrayindex))^);
end;
end;

"
Warning: not very well tested!

Martin
==================
Simplified version without dynarrayelesize:

"
type
integerarty = array of integer;
yourrecty = record
a: integer;
b: string;
c: integerarty;
end;
yourrecarty = array of yourrecty;

procedure deleteitem(var value: yourrecarty; const aindex: integer);
begin
if (aindex < 0) or (aindex > high(value)) then begin
tlist.Error(SListIndexError, aindex);
end;
if high(value) = 0 then begin
value:= nil;
end
else begin
finalize(value[aindex]);
move((pchar(value)+sizeof(value[0])*(aindex+1))^,
(pchar(value)+sizeof(value[0])*aindex)^,
sizeof(value[0])*(high(value)-aindex));
fillchar(value[high(value)],sizeof(value[0]),0);
setlength(value,high(value));
end;
end;
"

Martin
===============
Аватара пользователя
debi12345
долгожитель
 
Сообщения: 5761
Зарегистрирован: 10.05.2006 23:41:15
Откуда: Ташкент (Узбекистан)

Сообщение debi12345 » 11.09.2006 10:36:59

Продолжение :

> Do your versions consider possible memory fragmentation ?
> There're also special cases of dynarrays containing another arrays &
> dynamic objects in turn. Plain usage of "move" there turned incorrect
> because of fragmented ( uncontiniuos ) memory areas allocated to such
> complex types. For that, the people have been advised to use linked lists
> instead of dynarrays.

The used Procedure Finalize() frees strings and dynamic arrays in a record.
Classes must be freed by hand. The disadvantage of linked lists is that
there is no access by index (remember recno slowness in TBufDataset!).
The used move does not fragment memory, it shifts the items above the index
one down. Record fields of type dynamic array are always stored as a
pointer to the actual data, memory is fragmented anyway. The advantage is
that the memory size of the base array and therefore the size of the moved
memory area is small.

Martin
Аватара пользователя
debi12345
долгожитель
 
Сообщения: 5761
Зарегистрирован: 10.05.2006 23:41:15
Откуда: Ташкент (Узбекистан)

Сообщение jwv » 11.09.2006 10:37:32

ZW писал(а):надо выкидывать элементы из DS.d[i].x[j]

т.е. надо удалить элемент DS.d[i].x[j]?

Код: Выделить всё
procedure TForm1.Button1Click(Sender: TObject);
type
  Control = record
    speed: integer;
  end;

  DS1 = record
    path:array of integer;
    flagp:byte;
    temp:array of integer;
    etemp:array of Control;
  end;

  xarray = array of DS1;
  DS2 = record
    x: xarray;
    flagx:byte;
    Y:integer;
  end;

  DS3 = record
    D: array of DS2;
    flags: byte;
  end;

procedure DeleteItem(var arr: xarray; const index: integer);
var s, d: Pointer;
    iCount: integer;
begin
  if (index < low(arr)) or (index > high(arr)) then
    raise ERangeError.Create('индекс за пределами масива!');

  with arr[index] do begin //уменьшаем количество ссылок на масивы path, temp, etemp
    path := nil;
    temp := nil;
    etemp := nil;
  end;

  if index < high(arr) then begin //если не последний элемент, то перемещаем данные
    iCount := SizeOf(DS1);
    s := Pointer(Integer(arr) + (index + 1) * iCount); //откуда
    d := Pointer(Integer(arr) + index * iCount); //куда

    move(s^, d^, iCount * (high(arr)-index));
   
    fillchar(arr[high(arr)],sizeof(DS1),0); //prevent finalize

  end;

  SetLength(arr, high(arr));
end;

var DS: DS3;
begin
  SetLength(DS.D, 1);
  SetLength(DS.D[0].x, 3);
  DS.D[0].x[0].flagp := 1;
  DS.D[0].x[1].flagp := 2;
  DS.D[0].x[2].flagp := 3;

  DeleteItem(DS.D[0].x, 1); //удаляем второй эл. масива
  DeleteItem(DS.D[0].x, 1); //удаляем последний эл. масива
  DeleteItem(DS.D[0].x, 0); //удаляем единственный эл. масива
end;
Последний раз редактировалось jwv 11.09.2006 12:10:02, всего редактировалось 2 раз(а).
jwv
новенький
 
Сообщения: 21
Зарегистрирован: 10.05.2005 12:23:16

Сообщение jwv » 11.09.2006 11:51:49

debi12345 писал(а):Вот что сам Мартин (автор MSEgui ) пишет :

==============
To delete an item of a dynamic array of any type do:
...

procedure deleteitem(var value: yourrecarty; const aindex: integer);
...
fillchar(value[high(value)],sizeof(value[0]),0); //prevent finalize
setlength(value,high(value));
...
===============


точно :)
перед setLength в предыдущем посте надо добавить
fillchar(arr[high(arr)],sizeof(DS1),0); //prevent finalize
jwv
новенький
 
Сообщения: 21
Зарегистрирован: 10.05.2005 12:23:16

Сообщение Mirage » 11.09.2006 17:11:39

А зря. Есть в FPC такой баг-перестраховка на Var-аргументах :

--------------
proc ( var arg: array of integer; ..)
begin
..
setlength(arg, value)
..
end;
-------------

будет ругаться, зато :

----------------
type

integerarty = array of integer;

var
..

proc ( var arg: integerarty; ..)
begin
..
setlength(arg, value)
..
end;
-------------

работает на "ура"


Я тоже не сразу понял, почему автор MSEgui всегда объявляет новый тип для открытых массивов.


Ничего удивительного. Ведь
Код: Выделить всё
proc ( var arg: array of integer; ..)

и
Код: Выделить всё
type
  integerarty = array of integer;
proc ( var arg: integerarty; ..)

разные вещи.
Первое - Open Array Parameters. Может принимать в ккачестве параметров не только динамические массивы, но и обычные. Поэтому SetLength и не работает. Без модификатора var вообще передается через стек.

Второе же - ожидается передача именно дин. массива.

Не знаю как в FPC, а в Delphi этот "баг" прописан в документации и является фичей.:)

По теме: самый быстрый способ удалить из массива элемент это заместить его последним эл-том массива (предварительно можно финализировать), а сам массив, сократить на единицу.

Порядок эл-тов при этом конечно не сохраняется, но это далеко не всегда реально нужно. А все эти Move'ы с дин. массивами чреваты. Все-таки дин. массивы это refcounted объекты и не стоит с ними так.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Сообщение Cheb » 12.09.2006 11:23:38

А чем вас не устраивает банальная перестановка элемента с хвоста на место освобождаемого? Или порядок важен?
Аватара пользователя
Cheb
энтузиаст
 
Сообщения: 994
Зарегистрирован: 06.06.2005 15:54:34

Сообщение ZW » 12.09.2006 23:34:23

debi12345 писал(а):Вот что сам Мартин (автор MSEgui ) пишет :


Martin
==================
Simplified version without dynarrayelesize:

"
type
integerarty = array of integer;
yourrecty = record
a: integer;
b: string;
c: integerarty;
end;
yourrecarty = array of yourrecty;

procedure deleteitem(var value: yourrecarty; const aindex: integer);
begin
if (aindex < 0) or (aindex > high(value)) then begin
tlist.Error(SListIndexError, aindex);
end;
if high(value) = 0 then begin
value:= nil;
end
else begin
finalize(value[aindex]);
move((pchar(value)+sizeof(value[0])*(aindex+1))^,
(pchar(value)+sizeof(value[0])*aindex)^,
sizeof(value[0])*(high(value)-aindex));
fillchar(value[high(value)],sizeof(value[0]),0);
setlength(value,high(value));
end;
end;
"

Martin
===============


Опа... а вот это работает...
Правда есть два плохих момента:
1. почему то маты на SListIndexError
и самое плохое
2. я пока не понимаю как это работает:((((
Большое спасибо за подсказку...:)
ZW
новенький
 
Сообщения: 26
Зарегистрирован: 16.08.2006 15:11:23

Сообщение ZW » 13.09.2006 02:24:05

Совсем идиотский вопрос: как сделать полную копию динамического массива(или записи) в другую переменную, а не отзеркалировать адрес, по которому хранятся данные?
ZW
новенький
 
Сообщения: 26
Зарегистрирован: 16.08.2006 15:11:23

Сообщение ZW » 13.09.2006 09:03:55

Cheb писал(а):А чем вас не устраивает банальная перестановка элемента с хвоста на место освобождаемого? Или порядок важен?


Желательно сохранить порядок.
ZW
новенький
 
Сообщения: 26
Зарегистрирован: 16.08.2006 15:11:23

Сообщение jwv » 15.09.2006 12:51:02

ZW писал(а):Совсем идиотский вопрос: как сделать полную копию динамического массива(или записи) в другую переменную, а не отзеркалировать адрес, по которому хранятся данные?


если тип элемента масива простой (т.е. не дин. масив или длинная строка) например integer то можно с помощью move

Код: Выделить всё
type
  TMyType = record
    a, b: integer;
    s: ShortString;
  end;
  TArrayOfMyType = array of TMyType;

function CloneArrayOfMyType(const Arr: TArrayOfMyType): TArrayOfMyType;
begin
  SetLength(Result, length(Arr));
  move(Pointer(Arr)^, Pointer(Result)^, SizeOf(TMyType) * length(Arr));
end;


если в TMyType встречаеться дин.масив или длинная строка то тогда наверное поэлементного копирования не избежать :(
потому что после move "реальное" количество ссылок на них увеличивается, а "внутренний" счетчик ссылок нет, а когда "внутренний" счетчик до 0 дойдет, память занимаемая дин.масивом или длинной строкой вернётся в пул свободной памяти.
jwv
новенький
 
Сообщения: 21
Зарегистрирован: 10.05.2005 12:23:16

Сообщение Mirage » 15.09.2006 17:00:20

Эффективное всеучитывающее копироваание дин. массива осуществляет функция Copy. RTFM наконец.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Сообщение ZW » 15.09.2006 17:04:30

jwv писал(а):
если тип элемента масива простой (т.е. не дин. масив или длинная строка) например integer то можно с помощью move



Погоди, в случае статичных массивов должно работать присваивание


если в TMyType встречаеться дин.масив или длинная строка то тогда наверное поэлементного копирования не избежать :(
потому что после move "реальное" количество ссылок на них увеличивается, а "внутренний" счетчик ссылок нет, а когда "внутренний" счетчик до 0 дойдет, память занимаемая дин.масивом или длинной строкой вернётся в пул свободной памяти.


Если только пересчитать размер масивов и использовать эти значения для расчета для Move. Имеет смысл если массивы прямоугольные
ZW
новенький
 
Сообщения: 26
Зарегистрирован: 16.08.2006 15:11:23

Пред.След.

Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 7

Рейтинг@Mail.ru