2011-03-03 9 views
6

Necesito almacenar un número desconocido de grupos. Cada grupo tiene una cantidad desconocida de elementos/elementos. Este es mi 'grupo':¿Cómo almacenar matrices dinámicas en un TList?

TGroup= array of Integer;  <------ dynamic array (as you can see) :) 

Quiero usar un TList para mantener mis grupos. La idea es que quizás desee acceder a los grupos más tarde y agregarles más elementos.

tengo este código, pero no puedo hacer que funcione:

TYPE 
    TGroup= array of Integer;        // Each group has x items (x can be from 1 to 10000) 


procedure TForm1.FormCreate(Sender: TObject); 
VAR CurGroup: TGroup; 
    grp, item: Integer; 
    Groups: TList;          // can contain up to 1 million groups 
begin 
Groups:= TList.Create; 

{ Init } 
for grp:= 1 to 4 DO          // Put a dummy item in TList 
    begin 
    SetLength(CurGroup, 1);        // Create new group 
    Groups.Add(@CurGroup);         // Store it 
    end; 

CurGroup:= NIL;           // Prepare for next use 

for grp:= 1 to 4 DO          // We create 4 groups. Each group has 3 items 
    begin 
    CurGroup:= Groups[Groups.Count-1];     // We retrieve the current group from list in order to add more items to it 

    { We add few items } 
    for item:= 0 to 2 DO 
    begin 
     SetLength(CurGroup, Length(CurGroup)+1);   // reserve space for each new item added 
     CurGroup[item]:= Item; 
    end; 

    Groups[Groups.Count-1]:= @CurGroup;     // We put the group back into the list 
    end; 

{ Verify } 
CurGroup:= NIL; 
CurGroup:= Groups[0]; 
Assert(Length(CurGroup)> 0);        // FAIL 
if (CurGroup[0]= 0) 
AND (CurGroup[1]= 1) 
AND (CurGroup[2]= 2) 
then Application.ProcessMessages;       

FreeAndNil(Groups); 
end; 

Nota: El código es completa. Puedes pegarlo en tu Delphi (7) para probarlo.

+0

Y por "grupo" ¿te refieres a un magma asociativo con un elemento de identidad y tal que cada elemento tiene una inversa? –

+0

@Andreas - No. Para mí, el grupo es solo una lista (matriz) de números. Esta es mi definición de 'grupo': TGroup = array of Integer; – Ampere

Respuesta

4

He creado un contenedor alrededor de la matriz dinámica RTTI.

Es solo un primer borrador, pero está inspirado en su pregunta y en el hecho de que los métodos TList faltan.

type 
    TGroup: array of integer; 

var 
    Group: TGroup; 
    GroupA: TDynArray; 
    i, v: integer; 
begin 
    GroupA.Init(TypeInfo(TGroup),Group); // associate GroupA with Group 
    for i := 0 to 1000 do begin 
    v := i+1000; // need argument passed as a const variable 
    GroupA.Add(v); 
    end; 
    v := 1500; 
    if GroupA.IndexOf(v)<0 then // search by content 
    ShowMessage('Error: 1500 not found!'); 
    for i := GroupA.Count-1 downto 0 do 
    if i and 3=0 then 
     GroupA.Delete(i); // delete integer at index i 
end; 

Este TDynArray envoltorio trabajará también con la variedad de cadena o una tabla de registros Los registros ... sólo tienen que ser embalados y sólo tienen referencia a campos no contados (byte, entero, doble ...) o cadena de referencia campos contados (sin Variante ni Interfaz dentro).

El método IndexOf() buscará por contenido. Es decir, por ejemplo para una matriz de registro, todos los campos de registro (incluidas las cadenas) deben coincidir.

Consulte TDynArray en the SynCommons.pas unit en nuestro repositorio de código fuente. Funciona desde Delphi 6 hasta XE y maneja cadenas Unicode.

Y el método TTestLowLevelCommon._TDynArray son las pruebas unitarias automatizadas asociadas con esta envoltura. Aquí encontrará ejemplos de una matriz de registros y características más avanzadas.

estoy actualmente implementar SaveToStream y métodos LoadToStream ...

Tal vez una nueva forma de utilizar características genéricas como en todas las versiones de Delphi.

Editar:

He añadido algunos nuevos métodos a la TDynArray de grabación/objeto:

  • ahora se puede guardar y cargar un contenido matriz dinámica hacia o desde una cadena (usando LoadFromStream/SaveToStream o LoadFrom/SaveTo métodos) - utilizará un diseño de flujo binario patentado pero muy rápido;
  • y puede ordenar el contenido de la matriz dinámica de dos maneras: in situ (es decir, se intercambia el contenido de los elementos de la matriz) o mediante una matriz de búsqueda de índice entero externo (utilizando el método CreateOrderedIndex; en este caso, puede tener varios pedidos a la misma información);
  • puede especificar cualquier función de comparación personalizada, y existe un nuevo método Find que puede utilizar la búsqueda binaria rápida si está disponible.

Aquí es cómo esos nuevos métodos de trabajo:

var 
    Test: RawByteString; 
... 
    Test := GroupA.SaveTo; 
    GroupA.Clear; 
    GroupA.LoadFrom(Test); 
    GroupA.Compare := SortDynArrayInteger; 
    GroupA.Sort; 
    for i := 1 to GroupA.Count-1 do 
    if Group[i]<Group[i-1] then 
     ShowMessage('Error: unsorted!'); 
    v := 1500; 
    if GroupA.Find(v)<0 then // fast binary search 
    ShowMessage('Error: 1500 not found!'); 

Aún más cerca del paradigma genérico, más rápido, y para Delphi 6 hasta XE ...

+0

Ver http://synopse.info/forum/viewtopic.php? pid = 1522 –

6

Oh, esto sería mucho más agradable en las versiones más nuevas de Delphi ... Utilizarías el genérico, TList <T>. var Grupos: TList < Grupo TG >;

Lo mejor es usar otra matriz dinámica: Grupos: matriz de TGroup;

La razón es que las matrices dinámicas son compiladas y contadas por el compilador. TList solo opera en Punteros. No hay una forma directa de mantener los recuentos de referencia en buen estado al tratar de poner las dynarrays en el TList.

Otro problema que está teniendo es que está agregando la dirección de pila de la variable de matriz dinámica al TList, y no la matriz real. La expresión @CurGroup es la "dirección de la variable CurGroup" que al ser una variable local, está en la pila.

+0

+1. He dejado de encontrar soluciones para cosas que funcionan de la caja con un Delphi reciente. –

+0

No tengo Delphi 2010. – Ampere

+0

Utilicé 'array of TGroup' y funcionó. Pero simplemente 'actualicé' desde 'array of TGroup' como propones a TList. Quiero que funcione también con TList. – Ampere

4

no tengo D7 en una máquina aquí, pero usted podría intentar algo como esto en su lugar (aplicación de consola de prueba - se compila en XE con ninguna pista o advertencias, pero no sabe cómo D7 se harán cargo):

program Project1; 

{$APPTYPE CONSOLE} 

uses 
    SysUtils, Classes; 

type 
    TGroup = array of Integer; 
    THolder=class(TObject) 
    Group: TGroup; 
    end; 

var 
    GroupList: TList; 
    i: Integer; 

begin 
    GroupList := TList.Create; 
    for i := 0 to 2 do 
    begin 
    GroupList.Add(THolder.Create); 
    with THolder(GroupList[GroupList.Count - 1]) do 
    begin 
     SetLength(Group, 3); 
     Group[0] := i; 
     Group[1] := i + 10; 
     Group[2] := i + 20; 
    end; 
    end; 
    for i := 0 to GroupList.Count - 1 do 
    begin 
    Writeln(i, ':0 ', THolder(GroupList[i]).Group[0]); 
    Writeln(i, ':1 ', THolder(GroupList[i]).Group[1]); 
    Writeln(i, ':2 ', THolder(GroupList[i]).Group[2]); 
    end; 
    // Not cleaning up list content, because we're exiting the app. 
    // In a real app, of course, you'd want to free each THolder in 
    // the list, or use a TObjectList and let it own the objects. 
    // You'd also want a try..finally to make sure the list gets freed. 
    GroupList.Free; 
    Readln; 
end. 
+0

Para mí, un enfoque completamente razonable en Delphi 7. –

+0

¿Por qué no usó TObjectList directamente? –

+0

@ A.Bouchez, porque esa no era la pregunta que hizo el OP. La pregunta era específicamente sobre TList. Sin embargo, agregué la sugerencia para TObjectList como un comentario en el código. –

4

Otra cosa que puede querer probar es utilizar TObjectList. TObjectList puede almacenar una lista de Objetos, por lo que puede crear un nuevo descendiente de clase de TObject y luego administrar TObjectList usando estos objetos.

Algo así (no probado):

type TGroupArray : array of Integer; 

type TGroup = class(Tobject) 
    GroupArray : TGroupArray; 
end; 

GroupList : TobjectList; 

procedure TForm1.FormCreate(Sender: TObject);           
var CurGroup : TGroup; 
begin 

GroupList:= TObjectList.Create; 
CurGroup := TGroup.Create; 
SetLength(CurGroup.GroupArray,1); 
CurGroup.GroupArray[0] := 10; 

GroupList.Add(CurGroup); 

RetreiveGroup := GroupList.Items[0]; 
FreeandNil(GroupList); 
end; 

etc ...

+0

Lo haría de esta manera. Y cuando necesite agregar atributos como "Nombre: Cadena", puede agregarlo a TGroup, que es una clase. –

1

Cuando código:

for grp:= 1 to 4 DO          // Put a dummy item in TList 
    begin 
    SetLength(CurGroup, 1);        // Create new group 
    Groups.Add(@CurGroup);         // Store it 
    end; 

de hecho, SetLength (CurGroup) no creará un nuevo grupo . Cambiará el tamaño del único CurGroup existente.

Así que @CurGroup no cambiará: siempre habrá alguna dirección en la pila, donde se agrega CurGroup. Agrega la misma dirección a la lista varias veces.

por lo que usted tiene que crear una matriz dinámica de instancias tgroup, así:

var GroupArray: array of TGroup; 

SetLength(GroupArray,4); 
for grp := 0 to high(GroupArray) do 
begin 
    SetLength(GroupArray[grp],1); 
    Groups.Add(@GroupArray[grp]); 
end; 

Pero, por supuesto, la GroupArray debe permanecer asignado durante todos los grupos de tiempo va a necesitar. Así que quizás tenga que poner este GroupArray como una propiedad en la clase, porque si crea este GroupArray en la pila, todos los elementos GroupArray [] se liberarán cuando el método salga y se libere su pila.

Pero, por supuesto, la GroupArray [] será un acceso más directo a los artículos tgroup ... Grupos [i] será igual a GroupArray [] ... Con ningún problema recuento de referencias ... Porque, por ejemplo, si cambia el tamaño, por ejemplo, un elemento TGroup de su puntero en Grupos [], no estoy seguro de que no tenga ninguna pérdida de memoria ...

+0

Hola, Bouchez. "Tendrás que crear una matriz dinámica de instancias TGroup" - - - En realidad este es mi código inicial. Funciona perfectamente Solo quería "actualizar" a TList, que es menos complicado que una matriz, ya que tiene el método Add(). – Ampere

+0

"SetLength (CurGroup) NO creará un nuevo grupo" - - - Tiene razón. Grité el código de un programa más grande para que sea más fácil de leer. En ese código, la inicialización de la lista es realmente correcta, ya que no utilizo un bucle para inicializarla (no agrego el mismo elemento varias veces). - ¡Pero gracias de nuevo! – Ampere

0

Así que, básicamente, todo el mundo sugiere crear una matriz de matriz en lugar de usar un TList. Bueno, ya lo hice. Solo quería "actualizar" de 'array of array' a un TList ya que tiene Add(). Parece que volveré a mi código original. Gracias a todos y +1 por cada respuesta.

+1

Bueno, Allen sugiere el nuevo Delphi que hace que este tipo de código sea más limpio. –

+0

Sí. Lo sé. Pero no lo tengo. Entonces, esta solución no está disponible (para mí). – Ampere

+1

sugerí usar TObjectList – Simon

Cuestiones relacionadas