2012-01-22 6 views
0

デルファイのコレクションにランダムアクセスする - 私はその一部のアプリケーション、に取り組んでいます(意味は高度な概念は、すでに知られていることを期待していない)デルファイデザインのアイデア - 必要なデータ構造データ

- 初心者プログラマをスケジューリング機能。私は予定のグループを見る。これらの予定は特定の日付になります。私は各日付のすべての予定の統合ビューを作成する必要があります。これによって、私は1月17日についてのデータの「グループ化」を作成することを意味します。これは、開始日、停止時など、その日の予定の数になります。これは配列、クラス、この時点では分かりません。私は17日に1つを持っているかもしれませんし、次のものは22日、次に24は次の日です、そして、私は次の35日間毎日1つずつ持っているかもしれません...私は0か1の構造/ 1日あたりの容器数は3〜5ヶ月ですが、どれが存在していてどれが存在しないかは非常に流動的です。この構造は統合されたビューです。つまり、1つのアポイントメントから情報の一部を取得し、次のアポイントメントから情報の一部を取得するため、各アポイントメントを読み込んでいるので、この構造をすばやく見つける必要があります。

私はこれらの構造をメモリベースにする必要があるため、高速です。

要件 このような構造をオンザフライで作成できるようにする必要があります(たとえば、Janの予定を読んだだけです)。 22、構造を更新する必要がありますが、まだ存在していないので、その場で作成する必要があります)。

私はそれらを素早く見つける必要があります。

構造の各々は、複数のデータ型(ブール、のTDateTime、TStringListの、など)を保持する。

I HもしDelphi 2010が役に立ったら...

私はどんな構造を探していますか?すでにD2010(または無料)に同梱されている簡単で高速な操作がすべて重要です。

おかげ

GS

UPDATED INFO:

だから、使用する(私は...と思う)私が決めた ... TDictionaryまたはTObjectDictionaryが移動するための方法で表示されます私の基本情報を保持するためにレコードを作成し、これらのレコードをTDictionaryまたはTObjectDictionaryに格納します。私は両方の挑戦に走っています。 TDictionaryでは、私はそれらが(ポインタであるので)完了したときにレコードを解放する方法を理解することができず、TObjectDictionaryを使用して、レコードタイプでレコードを作成することはできません。コードサンプルが...私はTObjectDictionaryを行く場合、これは動作しません...

var 
    Dic : TObjectDictionary<Integer,TSummaryAppt>; 

begin 
    Dic := TObjectDictionary<Integer,TSummaryAppt>.Create([doOwnsKeys, doOwnsValues]); 

// Create the base record definition that will be put in the TObjectDictionary/TDictionary  

type 
    TSummaryAppt = record 
     Date : TDate; 
     SA_ID : Integer; 
     BusyFlag : Array[1..36] of Boolean; // 15 minute periods... 
     PCTFree: Double; 
     LargestFreeMinutes : integer; 
    end; 

ある作成ラインは無効なクラス型キャストと(細かいコンパイル)の実行に失敗しました。任意およびすべてのヘルプ感謝

var 
    Dic : TDictionary<Integer,TSummaryAppt>; 
    rec : ^TSummaryAppt; 
    p : TSummaryAppt; 
    i : Integer; 

begin 
    Dic := TDictionary<Integer,TSummaryAppt>.Create; 

// Now add some records. THese have to be created dynamically 
// because I dont know at compile time how many there are. 

new(rec); 
rec.Date := now; 
rec.SA_ID := 3; 
Dic.Add(1, rec^); 

new(rec); 
rec^.Date := now; 
rec^.SA_ID := 5; 
Dic.Add(2, rec^); 

new(rec); 
rec^.Date := now; 
rec^.SA_ID := 7; 
Dic.Add(3, rec^); 

// Test ... 
    for p in Dic.Values do begin 
    ShowMessage(IntToStr(p.SA_ID)); 
    end; 

// Now free everything. HERE IS WHERE I AM HAVING PROBLEMS... 
// What should I be doing? 

for p in Dic.values do 
    p.dispose; 

    Dic.Values.Free; 
    Dic.Keys.Free; 
    Dic.Free; 

end; 

....

TDictionaryは少し親しみやすいように見えますが、私は私のメモリを解放しなければなりません。私はどうしたら違うはずですか? ありがとう! GS

+1

'TClientDataSet'を見ましたか?これはかなりメモリ内のデータベース(必要に応じてファイルバックアップストレージを持つことができます)、基本的に任意のデータ型をサポートし、インデックスをサポートしています。 –

+1

質問に対するあなたの編集は完全に不適切です。それは新しい質問であるはずです。それはレコードを割り当てるために 'New'を使うべきではないということを私はあなたに伝えることができます。辞書に「追加」をすると、レコードのコピーが作成されます。したがって、レコード型のローカル変数を使用し、そのローカルをローカルに渡すことを呼び出すことができます。とにかく、ここで編集を元に戻して、辞書の使用に関する具体的なヘルプが必要な場合は新しい質問をしてください。この提案には –

答えて

3

本当に新しい質問である必要がある質問への更新について、ここで行う必要があります。

まず、キーと値の両方が値タイプであるため、TDictionary<K,V>を使用する必要があります。アイテムを辞書に追加すると、キーと値の両方のコピーが作成されるため、動的割り当てを行う必要はありません。

あなたのコードは次のようになります。あなたが辞書で終わった

type 
    TSummaryAppt = record 
    Date: TDate; 
    SA_ID: Integer; 
    BusyFlag: Array[1..36] of Boolean; // 15 minute periods... 
    PCTFree: Double; 
    LargestFreeMinutes: Integer; 
    end; 

.... 

var 
    Dic: TDictionary<Integer, TSummaryAppt>; 
    rec: TSummaryAppt; 

.... 

// create the dictionary 
Dict := TDictionary<Integer, TSummaryAppt>.Create; 

.... 

// initialise rec, in your code you would put real values in 
FillChar(rec, SizeOf(rec), 0); 

rec.Date := now; 
rec.SA_ID := 3; 
Dict.Add(1, rec); 

rec.Date := now; 
rec.SA_ID := 5; 
Dict.Add(2, rec); 

//etc. 

あなたがする必要があるすべては、それは無料です。辞書はすべてのコンテンツを所有しており、クリーンアップされます。

Dict.Free; 

辞書の機能を包み込み、より高いレベルのインターフェイスで公開することをお勧めします。したがって、追加する値のすべてのフィールドをパラメータとして受け取ったAddメソッドがあるとします。また、変更可能なフィールドだけを受け取った更新メソッドが必要な場合もあります。

+0

値を入力すると(rec.date:= ...、rec.SA_ID:=など...)なぜこの例でFillCharを使用する必要がありますか? – user1009073

+0

すべてのフィールドに入力する場合は、 'FillChar'を使用する必要はありません。私は、ローカル変数が初期化されていないため、これを行いました。すべてのフィールドを入力します(または、レコードコンストラクターを呼び出すことをお勧めします)。 –

+0

私はこの答えが受け入れられることに恥ずかしいです。この答えが扱っている広がっている質問の特定の部分は、それのごく一部です。質問は2つに分割されていて、私の答えはフォローアップの質問に対して合理的であったはずです。 –

3

generic TDictionaryコレクションを知っていますか?

class TAppointmentCalendar = TDictionary<TDate, TAppointments> 
end; 

TAppointmentsもGenerics.Collections.TObjectListに基づいて、ジェネリッククラスとすることができる:

class TAppointments = TObjectList<TAppointment> 
end; 

必要に応じて、これらのクラスはプロパティまたはデータ集約メソッドを追加するために、例えば、拡張することができます。

その後

Cal := TAppointmentCalendar.Create; 

Cal.Add(MyDate, AppointmentsForThisDay); 

カレンダーをインスタンス化または予定

var 
    Appointments: TAppointments 
begin 
    Cal.TryGetValue(ADate, Appointments); 
... 

を取得TDictionaryは、ハッシュベースのキー検索を実行します。

+1

+1です。しかし、ほとんどの場合、辞書にオブジェクトを格納する予定がある場合は、自動的に所有オブジェクトを解放できるので、TObjectDictionaryを使用するほうがよいです。 – Linas

+1

しかし、このようなT *ディクショナリは直接ストレージを処理しません...それはOPのための良いアイデアであるかどうかは分かりません。手でシリアライズするのは簡単ではありません。 –

+0

Arnaudは、最高のデータストレージソリューションとデータ構造へのマッピングのどちらも、一般的なアプリケーション設計に大きく依存しています。 – mjn

2

まず、データ構造の初心者として、この素晴らしい本を購入して読む価値があります:The Tomes of Delphi: Algorithms and Data Structures - By Julian Bucknall

ここに、アプリケーションを実装するためのレイアウトがいくつかあります。

A. NoSQLデータベース。たとえば、BigTableオープンソースコンポーネントをご覧ください。

ルートコンポーネントTSynBigTableは、ファイルベースのデータベース内のレコードに使用できます。それは軽く、速度のために非常に最適化されています。

2人の子供がレコード内のフィールドを処理します。フィールドレイアウトはオンザフライで変更できます。 TSynBigTableRecord and TSynBigTableMetaDataを参照してください。

B.使用通常のSQLデータベース、いずれかのようなTClientDataSetまたはSQLを介したダイレクトは(あなたは不要外部DLL)を持つ、すなわち、(静的静的SQLite3のエンジンにいくつかのオープンソースコンポーネントを見つけることができます。

。。それは、FirefoxやChromeのような、あるいはほとんどの携帯電話のOSには、多くのプログラムで使用されて

クエリを高速に行うためには - 、アプリケーションデータとしてSQLite3のを非常に良いアイデアであることがthe main purpose of this libraryで使用いくつかの列(たとえば、日付フィールド)にインデックスを作成しなければならないため、クエリ結果はi mmediate。

「これらの構造をオンザフライで作成できる」ことはお勧めしません。これは良いプログラミング慣行ではありません - またはそれは非常に複雑になるでしょう:レコード内にフィールドレイアウトを格納する必要があるので、初心者のデルファイプログラマーのための良い道ではありません。

どのような種類のデータでも処理できるように、データ構造を十分に開いたほうがよいでしょう。 SQLite3を使用すると、データをBLOBまたはテキスト(JSONなど)としてシリアル化できます。これは、例えばです。 what we allowオープンソースmORMot ORM - これはクライアント - サーバーですが、単独で使用することもできます。フレームワークのドキュメント、特にテストドリブン、OR​​M、SOAなどの設計アプローチを提示しようとするSADドキュメントを見てみることをお勧めします。

cTDictionary種類のストレージが必要な場合は、同じメソッドを扱いますが、自動シリアライズや複数のインデックス(TDictionaryで処理されていない)などの独自の機能を備えたラッパー(TDynArrayラッパー)をご利用ください。 TDynArrayを使用して質問のUPDATE

いくつかのコードの後

:それだけ平文(ダブル、整数、ブール値を含むため

もちろん
type 
    TSummaryAppt = record 
     Date : TDate; 
     SA_ID : Integer; 
     BusyFlag : Array[1..36] of Boolean; // 15 minute periods... 
     PCTFree: Double; 
     LargestFreeMinutes : integer; 
    end; 
    TSummaryApptDynArray = array of TSummaryAppt; 

var rec: TSummaryAppt; 
    SAs: TSummaryApptDynArray; 
    SA: TDynArray; 
    F: TFileStream; 
begin 
    SA.Init(TypeInfo(TSummaryApptDynArray),SAs); 
    rec.Date := now; 
    rec.SA_ID := 3; 
    SA.Add(rec); // rec is now added in SAs[] 
    assert(length(SAs)=1); // or SA.Count=1 
    assert(SAs[0].SA_ID=3); 
    for rec in SAs do // will work like any dynamic array 
    ShowMessage(IntToStr(p.SA_ID)); 
    F := TFileStream.Create('datafile',fmCreate); 
    SA.SaveToStream(F); // a TDictionary won't do that 
    F.Free; 
    SA.Clear; 
    assert(length(SAs)=0); // or SA.Count=0 
    F := TFileStream.Create('datafile',fmOpenRead); 
    SA.LoadFromStream(F); // a TDictionary won't do that 
    F.Free; 
    assert(length(SAs)=1); // or SA.Count=1 
    assert(SAs[0].SA_ID=3); 
    for rec in SAs do // will work like any dynamic array 
    ShowMessage(IntToStr(p.SA_ID)); 
    // you need nothing to free the memory, since both are handled by the compiler 
end; 

、あなたのアレイは、1つのブロックに直接格納することができます);私たちのTDynArrayラッパーはまたはその中の他の動的配列を扱うことができます。

私たちのORMを使用していくつかのコードは:

type 
TBusyFlag = set (1..36); 
TSummaryAppt = class(TSQLRecord) 
private 
    fDate : TDate; 
    BusyFlag : TBusyFlag; // 15 minute periods... 
    PCTFree: Double; 
    LargestFreeMinutes : integer; 
    published 
    // already contains an ID: integer field 
    property Date : TDate read fDate write fDate; 
    property BusyFlag : TBusyFlag read fBusyFlag write fBusyFlag ; // 15 minute periods... 
    property PCTFree: Double read fPCTFree write fPCTFree; 
    property LargestFreeMinutes : integer read fLargestFreeMinutes write fLargestFreeMinutes; 
end; 

// then initialize the database model and use your database: 
Model := TSQLModel.Create([TSummaryAppt]); 
Client := TSQLRestClientDB.Create(Model,nil,'FileName',TSQLRestServerDB); 
Client.Server.CreateMissingTables(0); // will create the database if needed 
... 
rec := TSummaryAppt.Create; 
rec.Date := Now; 
rec.ID := 3; // but the ORM may create one unique ID for you 
Client.Add(rec); 
rec.Date := 0; 
Client.Retrieve(3,rec); 
...  
rec.Free; 
Client.Free; 
Model.Free; 

私たちのORMは、ここでのデータ保存のためのSQLite3のデータベースを作成し、すべて1つの実行可能で、ローカルで使用されています。しかし、TSQLRestClientDBTSQLite3HttpClientTSQLRestServerDB+TSQLite3HttpServerに変更すると、標準のJSON(およびAJAXアプリケーション)からリモートでデータを使用することができます。クライアントコードを変更することなくSQLite3以外のもの(たとえOracleやメモリ内のデータベースまで)でデータを保存したい場合は、そうすることができます。

+0

なぜ配列は辞書より優れていますか?正当化せずに、実際には非常に奇妙に見えます。私はあなたが以前の質問から現代の一般的なコレクションに古いスタイルのコードを好むことを知っています。しかし、オプションCは私に反しているようです。あるいは、このクラスは実際には辞書です。 –

+0

@DavidHeffernan提供されたリンクに従うと、 'TDynArray'は動的配列の周りの*ラッパー*であることがわかります。実際には、この配列にアクセスするメソッド、' TDictionary'のすべてのメソッドとmore *「TDictionary」よりも。 (FindAndUpdate、FindAndAddIfNotExisting'メソッドを含む)、シリアライズ(バイナリまたはJSONへの)、スライス、リバース、ネストされた配列を直接追加、挿入、削除、検索、並べ替え(複数の順序付きインデックスを含む)、ハッシュ動的な配列の最大のパフォーマンス問題を回避するために、外部の 'Count'変数を使用します。 –

+0

私はリンクをたどりましたが、そのような機能が利用可能であることは私には分かりませんでした。私は自己告白された初心者にこのようなオブジェクトを推薦するとは思わない。型安全性、インターフェイスで公開されているポインタの大量使用、ジェネリックの使用がないようです。 –