Delphi Design Idea - нужна структура данных для произвольного доступа к коллекциям данных - PullRequest
0 голосов
/ 22 января 2012

Delphi - программист NOVICE - (то есть не ожидайте, что продвинутые концепции уже известны)

Я работаю над приложением, частью которого является функция планирования. Я смотрю на группу встреч. Эти встречи назначаются на конкретную дату. Мне нужно создать сводный обзор всех встреч на каждую дату. Под этим я подразумеваю, что я создаю «группировку» данных о 17 января, это будет количество встреч на эту дату, когда они начинаются, когда они останавливаются и т. Д. Это может быть массив, запись, класс, не знаю, на данный момент. У меня может быть один для 17-го, затем следующий для 22-го, затем следующий для 24-го, и затем у меня может быть один каждый день в течение следующих 35 дней ... У меня будет 0 или 1 структура / контейнеров в день, и я вижу, что таких месяцев от 3 до 5 месяцев ... Какие существуют, а какие нет, будут очень текучими. Эта структура представляет собой консолидированное представление, означающее, что я получаю некоторую информацию от одной встречи, а затем некоторую информацию от следующей встречи, поэтому, поскольку я читаю каждую встречу, мне нужно быстро найти эту структуру.

Мне нужно, чтобы эти структуры основывались на памяти, чтобы они были быстрыми. Они МОГУТ быть разных размеров (в зависимости от количества встреч в этот день.

ТРЕБОВАНИЯ Итак, мне нужно иметь возможность создавать эти структуры на лету. (например, я только что прочитал встречу на 22 января, и мне нужно обновить структуру, но она еще не существует, поэтому мне нужно сделать это на лету).

Мне нужно быстро их найти. (Возможно хэш на TDate).

Каждая из структур будет содержать несколько типов данных (логическое значение, TDateTime, TStringlist и т. Д.).

У меня Delphi 2010, если это поможет ...

Какую структуру я ищу? Легко, быстро, уже включено в D2010 (или бесплатно) - все это важно.

Спасибо

GS

ОБНОВЛЕННАЯ ИНФОРМАЦИЯ:

Так что, похоже, TDictionary или TObjectDictionary - это то, что нужно ... Я решил (я думаю ...) использовать запись для хранения моей базовой информации, а затем сохранить эти записи в TDictionary или TObjectDictionary. Я сталкиваюсь с проблемой в обоих из них. В TDictionary я не могу понять, как освободить записи, когда я закончу с ними (так как они являются указателями), а с TObjectDictionary я не могу создать его с типом записи ... Любая помощь приветствуется. Примеры кода ...

// 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;

Если я пойду в TObjectDictionary, это не будет работать ...

var
  Dic : TObjectDictionary<Integer,TSummaryAppt>;

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

Строка Create завершается неудачно при выполнении (прекрасно компилируется) с Invalid Class Typecast.

TDictionary выглядит немного дружелюбнее, но я должен освободить память ....

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;

Любая помощь приветствуется. Что я должен делать по-другому? Спасибо! GS

Ответы [ 3 ]

3 голосов
/ 22 января 2012

Что касается вашего обновления вопроса (который действительно должен быть новым вопросом), вот что вам нужно сделать.

Прежде всего, поскольку и ваш ключ, и значение для словаря являются типами значений, которые вам необходимо использовать 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, который принимает в качестве параметров все поля добавляемого значения. И вам может понадобиться метод обновления, который получил только изменяемые поля.

3 голосов
/ 22 января 2012

Знаете ли вы общую коллекцию 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 выполняет поиск ключей на основе хеш-функции.

2 голосов
/ 22 января 2012

Прежде всего, как новичку в структурах данных, стоит купить и прочитать эту великую книгу: Томы Дельфи: Алгоритмы и структуры данных - Джулиан Бакнолл .

Вот несколько возможных вариантов реализации вашего приложения.

A. База данных NoSQL. Например, взгляните на наши BigTable компоненты с открытым исходным кодом.

Корневой компонент TSynBigTable доступен для записей в файловой базе данных. Это легкий и очень оптимизированный для скорости.

Двое из их детей обрабатывают поля в записях. Расположение полей может меняться на лету. См. TSynBigTableRecord и TSynBigTableMetaData.

B. Используйте обычную базу данных SQL, например TClientDataSet или напрямую через SQL (некоторые компоненты с открытым исходным кодом вы найдете на статическом SQLite3 движке (статическом, то есть без необходимости использования внешних dll).

Использование SQLite3 в качестве данных приложения - очень хорошая идея. Это основное назначение этой библиотеки - она ​​используется многими программами, такими как FireFox или Chrome, или даже в большинстве ОС мобильных телефонов.

Чтобы сделать запросы быстрыми, вам нужно будет создать индексы для некоторых столбцов (например, в поле даты), и поэтому результаты запроса будут немедленными.

Я не рекомендую «иметь возможность создавать эти структуры на лету». ИМХО, это не очень хорошая практика программирования - или она станет очень сложной: для этого нужно будет хранить макет поля в записях, так что это не хороший путь для начинающего программиста на Delphi.

Вам лучше сделать свои структуры данных достаточно открытыми, чтобы обрабатывать любые данные. С SQLite3 вы можете сериализовать ваши данные в виде BLOB или текста (например, с помощью JSON). Это, например, что мы допускаем в нашем Open Source mORMot ORM - это клиент-сервер, но может использоваться автономно. Я бы порекомендовал взглянуть на нашу документацию по фреймворку, особенно документ SAD, который пытается представить некоторый подход к проектированию, такой как тестирование, ORM или SOA.

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

ПОСЛЕ ОБНОВЛЕНИЯ ВОПРОСА

Некоторый код, использующий TDynArray:

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;

Конечно, ваш массив может храниться непосредственно в одном блоке, поскольку он содержит только простые данные (double, integer, booleans); но наша оболочка TDynArray может обрабатывать любой string или другой динамический массив внутри.

Какой-то код с использованием нашего 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 для хранения данных. Но если вы измените TSQLRestClientDB на TSQLite3HttpClient и TSQLRestServerDB+TSQLite3HttpServer, вы сможете использовать свои данные удаленно, через стандартный JSON (а также из приложения AJAX). Без изменения вашего клиентского кода. И если вы хотите хранить ваши данные с чем-то другим, кроме SQLite3 (даже до Oracle или базы данных в памяти), вы можете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...