2010-12-07 5 views
10

Estoy implementando una matriz N x M (clase) con un registro y una matriz dinámica interna como la siguiente.Copia profunda de un registro con R1: = R2, o ¿Hay una buena manera de implementar la matriz NxM con el registro?

TMat = record 
public  
    // contents 
    _Elem: array of array of Double; 

    // 
    procedure SetSize(Row, Col: Integer); 

    procedure Add(const M: TMat); 
    procedure Subtract(const M: TMat); 
    function Multiply(const M: TMat): TMat; 
    //.. 
    class operator Add(A, B: TMat): TMat; 
    class operator Subtract(A, B: TMat): TMat; 
    //.. 
    class operator Implicit(A: TMat): TMat; // call assign inside proc. 
              // <--Self Implicit(which isn't be used in D2007, got compilation error in DelphiXE) 

    procedure Assign(const M: TMat); // copy _Elem inside proc. 
            // <-- I don't want to use it explicitly. 
end; 

Elijo un registro, porque no quiero Crear/Liberar/Asignar para usarlo.

Pero con la matriz dinámica, los valores no se pueden (profundamente) copiar con M1: = M2, en lugar de M1.Asignar (M2).

Intenté declarar el método de conversión implícito, pero no se puede usar para M1: = M2.

(Implícita (const pA: PMAT): TMAT y M1: M2 = @ funciona, pero es bastante feo y leer ..)

¿Hay alguna manera de conectar asignación de disco?

¿O hay alguna sugerencia para implementar la matriz N x M con los registros?

Gracias de antemano.

Editar:

que implementa, como a continuación con el método de Barry y confirmó que funcione correctamente.

type 
    TDDArray = array of array of Double; 

    TMat = record 
    private 
    procedure CopyElementsIfOthersRefer; 
    public 
    _Elem: TDDArray; 
    _FRefCounter: IInterface; 
    .. 
    end; 

procedure TMat.SetSize(const RowSize, ColSize: Integer); 
begin 
    SetLength(_Elem, RowSize, ColSize); 

    if not Assigned(_FRefCounter) then 
    _FRefCounter := TInterfacedObject.Create; 
end; 

procedure TMat.Assign(const Source: TMat); 
var 
    I: Integer; 
    SrcElem: TDDArray; 
begin 
    SrcElem := Source._Elem; // Allows self assign 

    SetLength(Self._Elem, 0, 0); 
    SetLength(Self._Elem, Length(SrcElem)); 

    for I := 0 to Length(SrcElem) - 1 do 
    begin 
    SetLength(Self._Elem[I], Length(SrcElem[I])); 
    Self._Elem[I] := Copy(SrcElem[I]); 
    end; 
end; 

procedure TMat.CopyElementsIfOthersRefer; 
begin 
    if (_FRefCounter as TInterfacedObject).RefCount > 1 then 
    begin 
    Self.Assign(Self); // Self Copy 
    end; 
end; 

Estoy de acuerdo en que no es eficiente. Solo usar Asignar con registro puro es absolutamente más rápido.

pero es bastante práctico y más fácil de leer. (Y interesante. :-)

Creo que es útil para el cálculo de iluminación o prototipos de pre-producción. ¿No es así?

Edit2:

kibab da la función de conseguir el contador de referencia de la misma matriz dinámica.

La solución de Barry es más independiente de la impl interna, y quizás funcione en los próximos compiladores de 64 bits sin ninguna modificación, pero en este caso, prefiero la simplicidad & de kibab por su simplicidad. Gracias.

TMat = record 
    private 
    procedure CopyElementsIfOthersRefer; 
    public 
    _Elem: TDDArray; 
    .. 
    end; 

procedure TMat.SetSize(const RowSize, ColSize: Integer); 
begin 
    SetLength(_Elem, RowSize, ColSize); 
end;  

function GetDynArrayRefCnt(const ADynArray): Longword; 
begin 
    if Pointer(ADynArray) = nil then 
    Result := 1 {or 0, depending what you need} 
    else 
    Result := PLongword(Longword(ADynArray) - 8)^; 
end; 

procedure TMat.CopyElementsIfOthersRefer; 
begin 
    if GetDynArrayRefCnt(_Elem) > 1 then 
    Self.Assign(Self); 
end; 
+1

+1 pregunta interesante. Creo que me quedaré con los tipos de valores en un registro con sobrecarga del operador. –

Respuesta

8

Se puede utilizar una interfaz campo de referencia dentro de su registro para averiguar si su matriz está compartida por más de un registro: simplemente verifique el recuento de referencias en el objeto detrás de la interfaz, y sabrá que la d ata en las matrices es compartido.De esta forma, puede copiar de forma perezosa la modificación, pero aún así utilizar el intercambio de datos cuando las matrices no se están modificando.

+0

+1 ¡Fantástico! ¡Ahora voy a mirar mi código y ver si puedo hacerlo de esta manera! ¡Tira tu propia copia al escribir! –

+0

¡Ah, copia floja! ¡Ya veo! Solo insertando la rutina Check'nCopy antes de todo código de auto modificación. Trataré de implementar con esta técnica. Muchas gracias! – benok

+1

+1 para tener una idea. Pero la interfaz para esto no es necesaria en mi humilde opinión (o me he perdido algo aquí?). El recuento de referencias de cualquier matriz dinámica se puede leer directamente de esta manera (si dyn-array se comparte, devolverá valor> 1): function GetDynArrayRefCnt (const ADynArray): Longword; begin if Puntero (ADynArray) = nil luego Resultado: = 1 {o 0, dependiendo de lo que necesite} else Resultado: = PLongword (Longword (ADynArray) - 8) ^; final; – kibab

4

No puede anular la asignación de registros por operadores implícitos o explícitos. Lo mejor que puede hacer OMI no es utilizar la asignación directa, utilizando el método M.Assign lugar:

procedure TMat.Assign(const M: TMat); 
begin 
// "Copy" currently only copies the first dimension, 
// bug report is open - see comment by kibab 
// _Elem:= Copy(M._Elem); 
    .. 
end; 

ex

M1.Assign(M2); 

en lugar de

M1:= M2; 
+5

Tenga en cuenta que la copia no funciona con matrices multidim, http://qc.embarcadero.com/wc/qcmain.aspx?d=20086 Solo se copia la primera dimensión, otras solo se referencian, y como matrices dinámicas son no CopyOnWrite (como cadenas), por lo que cambiar en uno, cambia también en otros (matrices 'copiadas'). Similar es probable que encuentre con la copia de registros. – kibab

+2

No hay matrices dinámicas multidimensionales; solo matrices de matrices, que son un concepto diferente (permite una forma dentada). ¿Por qué Copiar haría una copia profunda de los elementos de la matriz que está copiando, pero solo si esos elementos son matrices? (Básicamente, sugiero que este error está muy cerca de ser "como se diseñó"). –

+0

@Barry Kelly: No estoy de acuerdo. OMI: la implementación actual del procedimiento "Copiar" debería mejorarse para incluir una copia profunda de las matrices dinámicas o, de lo contrario, debería cambiarse todo el concepto de matrices dinámicas en Delphi (compatibilidad con CopyOnWrite). – kludg

2

Acabo de darme cuenta de una razón por la cual esta puede no ser una gran idea. Es cierto que el código de llamada se vuelve mucho más simple con la sobrecarga del operador. Pero puede tener problemas de rendimiento.

Considere, por ejemplo, el código simple A := A+B; y suponga que utiliza la idea en la respuesta aceptada de Barry. Al utilizar la sobrecarga del operador, esta simple operación dará como resultado la asignación de una nueva matriz dinámica. En realidad, desearía realizar esta operación en su lugar.

Dichas operaciones en el lugar son muy comunes en los algoritmos de matriz de álgebra lineal por la sencilla razón de que no desea golpear al montón si puede evitarlo, es caro.

Para tipos de valores pequeños (por ejemplo, números complejos, matrices 3x3, etc.), la sobrecarga del operador dentro de registros es eficiente, pero creo que si el rendimiento es importante, la sobrecarga de operadores no es la mejor solución para matrices grandes.

+0

Sí, estoy de acuerdo. La sobrecarga del operador es solo una sintaxis azucarada y no eficiente. La creación rápida de prototipos con una forma sencilla y la optimización con la forma correcta es mi estrategia favorita (pero peligrosa para el tiempo fuera :-). – benok

+1

@benok: la sobrecarga del operador es eficiente para tipos de valores pequeños. Acabamos de convertir números de matriz compleja, 3 vectores, 3x3 a la sobrecarga del operador y no hemos perdido nada en el rendimiento: el código objeto es realmente idéntico. Simplemente no creo que vaya a funcionar bien con tipos de referencia en lugar de tipos de valores. –

Cuestiones relacionadas