2010-12-07 1 views
10

私は以下のようなレコードと内部ダイナミック配列を持つN×Mの行列(クラス)を実装しています。R1:= R2のレコードのディープコピー、またはレコードのあるNxMマトリックスを実装する良い方法はありますか?

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; 

レコードを選択するのは、作成/使用/割り当てを使用しないためです。

動的配列では、M1.Assign(M2)の代わりにM1:= M2で値を(深く)コピーすることはできません。

私は自己暗黙の変換方法を宣言しようとしましたが、M1:= M2では使用できません。

(暗黙(のconstのpA:PMAT):TMATとM1:= @ M2は動作しますが、それはかなり醜いと読めないのです...)

レコードの割り当てをフックする方法はありますか?

レコードがあるN×M行列を実装する方法はありますか?

ありがとうございます。

編集:

私はバリーの方法で以下のように実装され、正常に動作を確認しました。

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; 

私はそれが効率的ではないと同意します。純粋なレコードで割り当てを使用するだけで、絶対に高速です。

しかし、それは。かなり便利で、より読みやすいです(と面白い。:-)

私はそれが軽い計算やプリプロダクションのプロトタイピングに便利だと思います。ではない ?

EDIT2:

kibabは、動的配列自体の機能になっ参照カウントを与えます。

バリーのソリューションは内部のインプラントから独立しており、今後の64ビットコンパイラでは何も変更することなく動作するかもしれませんが、このケースでは効率が良いのでキバブを好むのが&です。ありがとう。

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興味深い質問です。私は演算子のオーバーロードを伴うレコードの値の型に固執すると思います。 –

答えて

8

あなたがインターフェイスを使用することができますあなたのレコード内のフィールド参照を使用して、複数のレコードで配列が共有されているかどうかを調べることができます。インターフェイスの背後にあるオブジェクトの参照カウントをチェックするだけで、dアレイ内のataは共有されます。こうすることで、変更を遅らせることができますが、行列が変更されていないときにもデータ共有を使用できます。

+0

+1素晴らしい!私は今、私のコードを見て、私はそれをこのようにすることができるかどうかを見ています!書き込み時に自分のコピーをロールバックしてください! –

+0

ああ、怠惰なコピー!そうですか!すべての自己修正コードの前にCheck'nCopyルーチンを挿入するだけです。私はこの手法で実装しようとします。どうもありがとうございます!アイデアのために+1 – benok

+1

+1。しかし、これのためのインタフェースはIMHOの必要はありませんオーバーヘッド(または私はここでsthを逃した?)。動的配列の参照カウントは、このように直接読み取ることができます(dyn-arrayが共有されている場合は、値> 1を返します)。function GetDynArrayRefCnt(const ADynArray):Longword; begin ポインタ(ADynArray)= nilの場合 結果:= 1 {または0、必要なものに応じて} else 結果:= PLongword(ロングワード(ADynArray) - 8)^; end; – kibab

4

暗黙的または明示的な演算子によるレコード割り当てを無効にすることはできません。あなたはIMOを行うことができます 最善ではなくM.Assignメソッドを使用して、直接割り当てを使用することではありません。

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; 

M1.Assign(M2); 

代わりの

M1:= M2; 
+5

マルチディムアレイではコピーが機能しないことに注意してください。http://qc.embarcadero.com/wc/qcmain.aspx?d=20086最初のディメンションのみがコピーされ、その他は参照され、ダイナミックアレイは(文字列のような)CopyOnWriteではなく、他の(コピーされた)配列でも変更されます。同様に、あなたはおそらくレコードをコピーして見つけるでしょう。 – kibab

+2

多次元の動的配列はありません。異なるコンセプトのアレイの配列のみ(ぎざぎざのある形状を可能にする)。なぜコピーは、コピーしている配列の要素に対してディープコピーを行うのですか?ただし、それらの要素が配列の場合のみです。 (基本的には、このバグが "設計通り"に近いことを示唆しています) –

+0

@Barry Kelly:私は同意しません。 IMOの "Copy"プロシージャの現在の実装は、動的配列のディープコピーを含むように改善する必要があります。そうしないと、Delphiの動的配列の概念全体を変更する必要があります(CopyOnWriteサポート)。 – kludg

2

私はちょうどこれが素晴らしい考えではないかもしれない理由を理解しました。演算子のオーバーロードによって呼び出しコードがはるかに簡単になることは事実です。しかし、パフォーマンスの問題があるかもしれません。

たとえば、単純なコードA := A+B;を考えて、その考えをバリーの受け入れられた答えに使用するとします。演算子のオーバーロードを使用すると、この単純な操作によって新しい動的配列が割り当てられます。実際には、この操作をその場で実行したいと考えています。

このようなインプレイス演算は、線形代数行列アルゴリズムでは非常に一般的です。なぜなら、ヒープを避けることができないという単純な理由です。高価です。

小さい値の型(たとえば、複素数、3x3行列など)の場合、レコード内の演算子のオーバーロードは効率的ですが、パフォーマンスが重要な場合は、演算子のオーバーロードが最適な解決策ではありません。

+0

はい、同意します。演算子のオーバーロードはシンタックスシュガーであり効率的ではありません。簡単な方法で高速プロトタイプを作成し、適切な方法で最適化することは私の好きなことです(しかし、時間外のrunnnigにとっては危険です:-)。 – benok

+1

@benok:演算子のオーバーロードは、小さな値の型に対して効率的です。我々は、複素数、3ベクトル、3x3の行列型を演算子のオーバーロードに変換し、パフォーマンスにおいて何も失わず、オブジェクトコードは実際には同一です。私は値型ではなく参照型でうまくいくとは思わない。 –

関連する問題