Есть ли способ добавить 2 массива в один? - PullRequest
10 голосов
/ 18 августа 2011

Есть ли простой универсальный способ добавить 2 массива в один? В приведенном ниже случае просто невозможно использовать оператор C := A + B ... Я бы не хотел делать это каждый раз.

TPerson = record
    Birthday: Tdate;
    Name, Surname:string;
end;

Tpeople = array of TPerson;

var A, B, C:Tpeople;

C:=A+B; // it is not possible

* 1005 спасибо *

Ответы [ 5 ]

11 голосов
/ 18 августа 2011

Из-за двух полей string в каждой записи TPerson вы не можете просто использовать двоичный «ход», так как вы испортите счет ссылок string - особенно в многопоточной среде.

Вы можете сделать это вручную - это быстро и приятно:

TPerson = record
  Birthday: TDate;
  Name, Surname: string;
end;

TPeople = array of TPerson;

var A, B, C: TPeople;

// do C:=A+B
procedure Sum(const A,B: TPeople; var C: TPeople);
begin
var i, nA,nB: integer;
begin
  nA := length(A);
  nB := length(B);
  SetLength(C,nA+nB);
  for i := 0 to nA-1 do
    C[i] := A[i];
  for i := 0 to nB-1 do
    C[i+nA] := B[i];
end;

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

procedure AddToArray(var A: TPeople; const B: TPeople);
var DA: TDynArray;
begin
  DA.Init(TypeInfo(TPeople),A);
  DA.AddArray(B); // A := A+B
end;

Метод AddArray может добавить подпорт исходного массива:

/// add elements from a given dynamic array
// - the supplied source DynArray MUST be of the same exact type as the
// current used for this TDynArray
// - you can specify the start index and the number of items to take from
// the source dynamic array (leave as -1 to add till the end)
procedure AddArray(const DynArray; aStartIndex: integer=0; aCount: integer=-1);

Обратите внимание, что для таких записей будет использоваться функция System._CopyRecord RTL, которая не так оптимизирована для скорости. Я написал более быструю версию - см. эту статью блога или эту ветку форума .

Если вы используете динамические массивы в функциях / процедурах, не забудьте явно использовать параметры const или var (как я кодировал выше), в противном случае он будет делать временную копию при каждом вызове, поэтому это может быть медленно.

3 голосов
/ 18 августа 2011

Ничего встроенного, что позволяет объединять динамические массивы.

Вы можете рассмотреть возможность использования одного из общих контейнерных классов, найденных в Generics.Collections, TList.

В вашем случае у вас будет 3 экземпляра TList, скажем A, B и C. Тогда вы можете написать

A.Clear;
A.AddRange(B);
A.AddRange(C);

Я думаю, это как можно ближе к тому, что вы хотите с тем, что доставлено из коробки.

Если вы готовы немного заняться кодированием, вы можете использовать перегрузку операторов, чтобы использовать именно тот синтаксис, который вам необходим. Объявите запись, содержащую массив TPerson с закрытой видимостью. Затем вам нужно реализовать оператор Add, свойство Count и свойство Items [] по умолчанию. Это тоже можно сделать универсальным, так что вам нужно написать его только один раз.

TTurboArray = record<T>
private
  FItems: array of T;
  //property accessors here
 public
   class operator Add(a, b: TTurboArray<T>): TTurboArray<T>;
   property Count: Integer read GetCount write SetCount;
   property Items[Index: Integer]: T read GetItem write SetItem; default;
end;

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

1 голос
/ 02 июля 2013

Вот как я справился с этим, хотя он потребовал небольшой (но, надеюсь, несущественной) модификации вашего исходного кода для использования TArray:

(протестировано в XE2)

uses
  Generics.Collections;

type
  TArrayExt = class(TArray)
    class function Concat<T>(const First, Second: array of T): TArray<T>; overload;
  end;

class function TArrayExt.Concat<T>(const First, Second: array of T): TArray<T>;
var
  i: Integer;
  l: Integer;
begin
  l := Length(First);
  SetLength(Result, l + Length(Second));
  for i := Low(First) to High(First) do
    Result[i] := First[i];
  for i := Low(Second) to High(Second) do
    Result[l + i] := Second[i];
end;

type
  TPerson = record
    Birthday: TDate;
    Name, Surname: String;
  end;

  TPeople = TArray<TPerson>;

var
  A, B, C: TPeople;
begin
  C := TArrayExt.Concat<TPerson>(A, B);

Основным отличием здесь является то, что я использую «TArray», а не «массив TPerson».Это может быть использовано для массивов строк, записей и т. Д. Я считаю, что основное преимущество такой работы заключается в том, что она действительно делает копию, а не перемещает.И я использую «нормальные» функции Delphi вместо таких вещей, как объемные копии памяти, которые могут дать мне волю.

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

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

1 голос
/ 18 августа 2011

Есть быстрый и грязный способ сделать это.Это ужасный хак, но он должен сработать и даже позаботиться о подсчете ссылок:

function ConcatPeople(const A, B: TPeople): TPeople;
var
  Temp: TPeople;
  ALen, BLen: Integer;
begin
  Result := Copy(A);
  BLen := Length(B);
  if BLen = 0 then
    Exit;
  ALen := Length(A);
  Temp := Copy(B);
  SetLength(Result, ALen + BLen);
  Move(Temp[0], Result[ALen], BLen * SizeOf(B[0]));
  FillChar(Temp[0], BLen * SizeOf(B[0]), 0);
end;

По сути, данные в Temp «обмениваются» с пустыми записями в Result, поэтому баланс сохраняетсяи пересчет продолжит работать.

Обновление

Для чего это стоит: Очевидно, это тот же метод, который использовался в принятом SO-ответе и в, например, TList .Вмонтировать.Я удалил этот ответ, но все еще думаю, что он действителен, поэтому я снова удалил его.Это может быть сделано с блокировкой вокруг блока Move / FillChar, поэтому никто не получает доступ к элементам, когда они «меняются местами».Я добавлю это.

0 голосов
/ 23 мая 2018

Ваш код отлично работает в новейшей версии delphi C := A+B;.

Но для динамических массивов в старых версиях вы можете использовать функцию concat. Пример:

C := Concat(A, B);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...