Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Унифицированный способ сравнивать массивы? [D7, WinXP]


alex_ant ©   (2007-09-30 17:15:00) 

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

К примеру, в Perl существует возможность сравнить массивы любого типа на идентичность, передав ссылки на них в качестве параметра в одну унифицированную функцию. Далее функция сама выясняет их типизацию и размерность, и возвращает истину, если количество элементов и их тип и значение совпадают. Можно ли так же в Delphi сделать?

Заранее благодарю за советы.


Riply   (2007-10-01 06:37:00) [1]

> [0] alex_ant ©   (30.09.07 17:15)

http://www.rsdn.ru/article/Delphi/dynarrays.xml
P.S.
Если внимательно прочитать эту статью и разобраться, то все получиться :)


oxffff   (2007-09-30 20:01:00) [2]


> alex_ant ©   (30.09.07 19:29) [17]
>
> > Или это скрытый вопрос "почему для строк не работает"?
>
>
>
> :) Да, это был скрытый вопрос. Прошу прощения, не обратил
> внимания на слово «строки».
>
> В общем, общий вывод, наверное, заключается в том, что проще
> делать поэлементное сравнение, а Delphi не позволяет «железно»
> сравнивать переменные неизвестного типа. Эх...


TypeInfo?


vpbar   (2007-09-30 19:38:00) [3]

>>alex_ant ©   (30.09.07 19:29) [17]
Дело в том что в этом (array of string) в массиве хранятся указатели (строка это по сути указатель).
Здесь  
B[0] := '1';
A[0] := '1';
память будет выделятся для каждой строки и следовательно указатели (строки B[0],A[0]) будут разные.
Надеюсь понятно.
В общем случае вывод правильный. В частном случае (массив простых типов) быстрее и проще как я указал или как в [15]. Но в при этом можно по ошибке передать массив не того типа.


alex_ant   (2007-09-30 19:29:00) [4]


> Или это скрытый вопрос "почему для строк не работает"?


:) Да, это был скрытый вопрос. Прошу прощения, не обратил внимания на слово «строки».

В общем, общий вывод, наверное, заключается в том, что проще делать поэлементное сравнение, а Delphi не позволяет «железно» сравнивать переменные неизвестного типа. Эх...


vpbar   (2007-09-30 19:09:00) [5]

>>alex_ant ©   (30.09.07 19:08) [15]
Читал [6] ??
Если элементы массива - суть ссылки на объекты (строки, классы), то естественно не работает.
Или это скрытый вопрос "почему для строк не работает"?


alex_ant   (2007-09-30 19:08:00) [6]

Огромное спасибо вам за советы. Попробовал такой код:


var
 PA, PB: Pointer;
 A, B: array of string;
begin
 SetLength(A, 2);
 A[0] := '1';
 A[1] := '2';
 SetLength(B, 2);
 B[0] := '1';
 B[1] := '2';

 PA := Pointer(A);
 PB := Pointer(B);

 if CompareMem(PA, PB, Length(A)) then
   ShowMessage('Равны')
 else
   ShowMessage('НЕ Равны');


Для массивов Integer работает, для массивов строк — нет.


vpbar   (2007-09-30 19:00:00) [7]

Dib@zol ©   (30.09.07 18:37) [12]
Ага. Именно так. Доволен. Приятно что собеседник умен.

Anatoly Podgoretsky ©   (30.09.07 18:41) [13]
ну в данном случае оба решения нетипобезопастны. И типобезопастных я не вижу. Это в дотнете наверно можно, а в делфи вряд ли.


Anatoly Podgoretsky   (2007-09-30 18:41:00) [8]

> Dib@zol  (30.09.2007 18:37:12)  [12]

Нет, поскольку нет никаких методов подтвердить, что указатель указывает на подобную структуру.


Dib@zol   (2007-09-30 18:37:00) [9]

> [11] vpbar ©   (30.09.07 18:21)

Через указатель-Pointer-адрес длину получить невозможно, за исключением тех случаев, когда это предусмотрено структурой хранящегося в памяти объекта, на которого поинтер указывает. Все довольны?


vpbar   (2007-09-30 18:21:00) [10]

Dib@zol ©   (30.09.07 18:07) [9]
запусти код из [6] . Работает? Хотя в Сompare длина массивов не передается. Истина проверяется практикой (с) Классик.
А по поводу. "Ну и как же?" Посмотри что такое динамический массив и строка. Это адрес блока. А четыре байта перед указателем - это длина блока. (Строка еще нулем заканчивается).
"Ведь что такое Pointer? " -Pointer - это Указатель(Pointer по англ). Т.е. вещь в себе. Pointer - это четыре байта в которых должен содержаться адрес в памяти. А что там по этому адресу - это только творцу(программеру) известно. Может массив, а может объект.


Dib@zol   (2007-09-30 18:13:00) [11]

> result:=CompareMem(a,b,Len);

Хмм. А вот и решение! Ну ладно, будем считать, что у меня просто показан принцип...


Dib@zol   (2007-09-30 18:07:00) [12]

> Гы. А я получил.

Ну и как же? А? Ведь что такое Pointer? Это ссылка на начало (первый байт) блока данных в адресном пространстве проги. И длиться этот блок может хоть до $7FFFFFFF. Так что ты не прав. Допустим есть у нас "голое" содержимое RAM. И есть первый байт (выделен). И как узнать какой байт является последним?

-d FFF2:93F1
FFF2:93F0     BF 5C 00 8A 1C F3 A4-33 C0 AB AB 59 5E 1F 1E    .\.....3...Y^..
FFF2:9400  56 C5 740A 8A 3C F3 A4-AB AB 5E 1F C5 74 02 80   V.t..


vpbar   (2007-09-30 18:05:00) [13]

Dib@zol ©   (30.09.07 17:52) [5]
кстати ваш вариант проще сделать так
function MegaCompare(A, B : Pointer; Len:Cardinal):boolean;
begin
result:=CompareMem(a,b,Len);
end;


vpbar   (2007-09-30 17:59:00) [14]

>>Dib@zol ©   (30.09.07 17:52) [5]
>>Через Пойнтер её не получить.
Гы. А я получил. Вот только размер и тип елемента никак (точнее сложно) ибо он известен только при компиляции.


vpbar   (2007-09-30 17:57:00) [15]

procedure TForm1.Button1Click(Sender: TObject);

function Сompare(a,b:pointer;sizeOfElement:byte):Boolean;
var La,Lb,j:integer;
   ca,cb:Pchar;
begin
result:=false;
if (a=nil)or(b=nil)then exit;
La:=PInteger(Integer(a)-4)^*sizeOfElement;
Lb:=PInteger(Integer(b)-4)^*sizeOfElement;
if La<>Lb then exit;
Ca:=Pchar(a);
Cb:=Pchar(b);
result:=CompareMem(a,b,La)
end;

var x,y:array of byte;
i:integer;
begin
SetLength(x,3);
for i:=low(x) to High(x) do  x[i]:=(i+1);
SetLength(y,3);
for i:=low(y) to High(y) do  y[i]:=(i+1);
//y[2]:=5;

if compare(x,y,sizeof(x[666])) then
caption:='Равны' else Caption:='Не равны';
end;

compare - сравнивает два динамических массива (можно и строки если вызывать так compare(@s1[1],@s2[1],1)) тип массива должен быть одинаковый. Если элементы массива - суть ссылки на объекты (строки, классы), то естественно не работает.


Dib@zol   (2007-09-30 17:52:00) [16]

Хз. Вот щас только что написал. Не тестировал, но теоретически должно сработать. Len - это длина большего из массивов. Находится с помощью length(Массив). Через Пойнтер её не получить. А, да - если один из массивов короче чем Len, то либо возникнет AV, либо продолжение массива будет случайным образом браться из памяти.

function MegaCompare(A, B : Pointer; Len:Cardinal):boolean;
var
 b1, b2 : Byte;
 i, k : Cardinal;
begin
 Result:=true;
 k:=Len-1;
 for i:=0 to k do begin
   CopyMemory(@b1, Pointer(longInt(A)+i), 1);
   CopyMemory(@b2, Pointer(longInt(B)+i), 1);
   if b1<>b2 then begin
     Result:=false;
     break;
   end;
 end;
end;


tesseract   (2007-09-30 17:48:00) [17]

Xor по размеру массива?


alex_ant   (2007-09-30 17:36:00) [18]


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


А как это можно сделать? Можете показать небольшой пример?


Dib@zol   (2007-09-30 17:25:00) [19]

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


alex_ant   (2007-09-30 17:23:00) [20]

Понял тут, что вопрос фактически является вопросом о возможности сравнить переменную любого типа (объект, массив, запись и пр.) с любой другой переменной...

Типа

if MegaEqual(PVarA, PVarB: Pointer) then
 ShowMessage('Переменные равны');
else
 ShowMessage('Переменные не равны');

где PVarA, PVarB — указатели на переменные любого типа.

P. S. Не судите строго, если что-то не так, с указателями работал очень мало.


Наверх

Титульная страница Поиск, карта сайта Написать письмо