树状数据结构(与 VirtualTreeview 一起使用)

发布于 2024-10-25 03:56:12 字数 771 浏览 1 评论 0原文

我已经到了需要停止将数据存储在 VCL 组件中并拥有“底层数据结构”的地步,如 先生。 Rob Kennedy建议。

首先,这个问题是关于“我如何制作一个底层数据结构”。 :)

我的层次结构由 2 级节点组成。

现在,我通过循环根节点来遍历我的东西,其中我循环遍历根节点的子节点,以获得我需要的东西(数据)。我希望能够将所有数据存储在所谓的底层数据结构中,以便我可以使用线程轻松修改条目(我想我能够做到这一点?)

但是,当循环访问我的条目时(现在 ) ),结果取决于节点的 Checkstate - 如果我使用底层数据结构,我如何知道我的节点是否已检查,何时循环遍历我的数据结构而不是我的节点?

假设我想使用 2 个级别。

这将是父母:

TRoot = Record
  RootName : String;
  RootId : Integer;
  Kids : TList; //(of TKid)
End;

和孩子:

TKid = Record
  KidName : String;
  KidId : Integer;
End;

这基本上就是我现在所做的。评论指出这不是最好的解决方案,所以我愿意接受建议。 :)

我希望你能理解我的问题。 :)

谢谢!

I have come to the point where I need to stop storing my data in a VCL component, and have an "underlying datastructure", as Mr. Rob Kennedy suggested.

First of all, this question is about "how do I make an underlying datastructure". :)

My hierachy consists of 2 levels of nodes.

Right now, I go thru my stuff by looping rootnodes, wherein I loop thru the rootnode's childnodes, to get what I need (Data). I would love to be able to store all my data in a so-called Underlying Datastructure, so that I can easily modify the entries using threads (I suppose I am able to do that?)

However, when looping through my entries (right now), the results are depending on the node's Checkstate - if I am using an underlying data structure, how do I know if my node is checked or not, when its my datastructure I loop thru, and not my nodes?

Let's say I wanted to use 2 levels.

This would be the Parent:

TRoot = Record
  RootName : String;
  RootId : Integer;
  Kids : TList; //(of TKid)
End;

And the kid:

TKid = Record
  KidName : String;
  KidId : Integer;
End;

Thats basically what I do now. Comments state that this is not the best solution, so I am open to suggestions. :)

I hope you understand my question(s). :)

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

陌上芳菲 2024-11-01 03:56:12

您请求的数据结构非常简单,非常简单,我建议使用 Windows 提供的 TTreeView:它允许将文本和 ID 直接存储到树的节点中,无需额外的工作。


尽管我建议使用更简单的 TTreeView,但我仍将提供我对数据结构问题的看法。首先,我将使用,而不是记录。在非常短的代码示例中,您以一种非常不幸的方式混合记录和类:当您复制 TRoot 记录时(分配记录会生成完整的副本,因为记录始终被视为“值”) "),您没有对树进行“深层复制”:TRoot 的完整副本将包含与原始版本相同的 Kids:TList,因为类、与记录不同的是,引用:您正在处理引用的值。

当您拥有带有对象字段的记录时,另一个问题是生命周期管理:记录没有析构函数,因此您需要其他机制来释放拥有的对象(Kids :TList)。您可以将 TList 替换为 Tkid 数组,但是在传递怪物记录时您需要非常小心,因为您可能会在您最意想不到的时候结束对大量记录的深度复制。

在我看来,最谨慎的做法是将数据结构基于,而不是记录:类实例(对象)作为引用传递,因此您可以随意移动它们,而无需问题。您还可以获得内置的生命周期管理(析构函数

基类将如下所示。您会注意到它可以用作 Root 或 Kid,因为 Root 和 Kid 共享数据:两者都有名称和 ID:

TNodeClass = class
public
  Name: string;
  ID: Integer;
end;

如果此类用作 Root,则需要一种方法来存储 Kids 。我假设您使用的是 Delphi 2010+,因此您有泛型。这个类,带有一个列表,看起来像这样:

type
  TNode = class
  public
    ID: integer;
    Name: string;
    VTNode: PVirtualNode;
    Sub: TObjectList<TNode>;

    constructor Create(aName: string = ''; anID: integer = 0);
    destructor Destroy; override;
  end;

constructor TNode.Create(aName:string; anID: Integer);
begin
  Name := aName;
  ID := anID;

  Sub := TObjectList<TNode>.Create;
end;

destructor TNode.Destroy;
begin
  Sub.Free;
end;

您可能不会立即意识到这一点,但仅这个类就足以实现多级树!下面是一些用一些数据填充树的代码:

Root := TNode.Create;

// Create the Contacts leaf
Root.Sub.Add(TNode.Create('Contacts', -1));
// Add some contacts
Root.Sub[0].Sub.Add(TNode.Create('Abraham', 1));
Root.Sub[0].Sub.Add(TNode.Create('Lincoln', 2));

// Create the "Recent Calls" leaf
Root.Sub.Add(TNode.Create('Recent Calls', -1));
// Add some recent calls
Root.Sub[1].Sub.Add(TNode.Create('+00 (000) 00.00.00', 3));
Root.Sub[1].Sub.Add(TNode.Create('+00 (001) 12.34.56', 4));

您需要一个递归过程来使用此类型填充虚拟树视图:

procedure TForm1.AddNodestoTree(ParentNode: PVirtualNode; Node: TNode);
var SubNode: TNode;
    ThisNode: PVirtualNode;

begin
  ThisNode := VT.AddChild(ParentNode, Node); // This call adds a new TVirtualNode to the VT, and saves "Node" as the payload

  Node.VTNode := ThisNode; // Save the PVirtualNode for future reference. This is only an example,
                           // the same TNode might be registered multiple times in the same VT,
                           // so it would be associated with multiple PVirtualNode's.

  for SubNode in Node.Sub do
    AddNodestoTree(ThisNode, SubNode);
end;

// And start processing like this:
VT.NodeDataSize := SizeOf(Pointer); // Make sure we specify the size of the node's payload.
                                    // A variable holding an object reference in Delphi is actually
                                    // a pointer, so the node needs enough space to hold 1 pointer.
AddNodesToTree(nil, Root);

使用对象时,虚拟树中的不同节点可能具有与其关联的不同类型的对象。在我们的示例中,我们仅添加 TNode 类型的节点,但在现实世界中,您可能拥有 TContactTContactCategory、< code>TRecentCall,全部在一个 VT 中。您将使用 is 运算符来检查 VT 节点中对象的实际类型,如下所示:

procedure TForm1.VTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var PayloadObject:TObject;
    Node: TNode;
    Contact : TContact;      
    ContactCategory : TContactCategory;
begin
  PayloadObject := TObject(VT.GetNodeData(Node)^); // Extract the payload of the node as a TObject so
                                                   // we can check it's type before proceeding.
  if not Assigned(PayloadObject) then
    CellText := 'Bug: Node payload not assigned'
  else if PayloadObject is TNode then
    begin
      Node := TNode(PayloadObject); // We know it's a TNode, assign it to the proper var so we can easily work with it
      CellText := Node.Name;
    end
  else if PayloadObject is TContact then
    begin
      Contact := TContact(PayloadObject);
      CellText := Contact.FirstName + ' ' + Contact.LastName + ' (' + Contact.PhoneNumber + ')';
    end
  else if PayloadObject is TContactCategory then
    begin
      ContactCategory := TContactCategory(PayloadObject);
      CellText := ContactCategory.CategoryName + ' (' + IntToStr(ContactCategory.Contacts.Count) + ' contacts)';
    end
  else
    CellText := 'Bug: don''t know how to extract CellText from ' + PayloadObject.ClassName;
end;

以下是为什么将 VirtualNode 指针存储到节点实例的示例:

procedure TForm1.ButtonModifyClick(Sender: TObject);
begin
  Root.Sub[0].Sub[0].Name := 'Someone else'; // I'll modify the node itself
  VT.InvalidateNode(Root.Sub[0].Sub[0].VTNode); // and invalidate the tree; when displayed again, it will
                                                // show the updated text.
end;

您知道有一个工作示例一个简单的树形数据结构。您需要“增长”此数据结构以满足您的需求:可能性是无限的!为了给您一些想法和探索方向:

  • 您可以将 Name:string 转换为虚拟方法 GetText:string;virtual,然后创建 TNode 的专门后代 覆盖 GetText 以提供专门的行为。
  • 创建一个 TNode.AddPath(Path:string; ID:Integer) ,允许您执行 Root.AddPath('Contacts\Abraham', 1); - 也就是说,一种自动创建所有中间节点到最终节点的方法,以便轻松创建树。
  • PVirtualNode 包含到 TNode 本身中,以便您可以检查节点是否在虚拟树中“检查”。这将成为数据与 GUI 分离的桥梁。

The data structure you're requesting is very simple, it's so simple I'd recommend using the windows-provided TTreeView: it allows storing the text and an ID straight into the tree's node with no additional work.


Despite my recommendation to use the simpler TTreeView I'm going to provide my take on the data structure problem. First of all I'm going to use classes, not records. In your very short code sample you're mixing records and classes in a very unfrotunate way: When you make a copy of the TRoot record (assigning records makes complete copies, because records are allways treated as "values"), you're not making a "deep copy" of the tree: The complete copy of TRoot will contain the same Kids:TList as the original, because classes, unlike records, are references: you're coping the value of the reference.

An other problem when you have a record with an object field is life cycle management: A record doesn't have an destructor so you'll need an other mechanism to free the owned object (Kids:TList). You could replace the TList with an array of Tkid but then you'll need to be very careful when passing the monster record around, because you might end making deep copies of huge records when you least expect it.

In my opinion the most prudent thing to do is to base the data structure on classes, not records: class instances (objects) are passed around as references, so you can move them around all you want with no problems. You also get built-in life cycle management (the destructor)

The base class would look like this. You'll notice it can be used as either the Root or the Kid, because both Root and Kid share data: The both have a name and an ID:

TNodeClass = class
public
  Name: string;
  ID: Integer;
end;

If this class is used as an Root, it needs a way to store the Kids. I assume you're on Delphi 2010+, so you have generics. This class, complete with a list, looks like this:

type
  TNode = class
  public
    ID: integer;
    Name: string;
    VTNode: PVirtualNode;
    Sub: TObjectList<TNode>;

    constructor Create(aName: string = ''; anID: integer = 0);
    destructor Destroy; override;
  end;

constructor TNode.Create(aName:string; anID: Integer);
begin
  Name := aName;
  ID := anID;

  Sub := TObjectList<TNode>.Create;
end;

destructor TNode.Destroy;
begin
  Sub.Free;
end;

You might not immediately realize this, but this class alone is enough to implement a multi-level tree! Here's some code to fill up the tree with some data:

Root := TNode.Create;

// Create the Contacts leaf
Root.Sub.Add(TNode.Create('Contacts', -1));
// Add some contacts
Root.Sub[0].Sub.Add(TNode.Create('Abraham', 1));
Root.Sub[0].Sub.Add(TNode.Create('Lincoln', 2));

// Create the "Recent Calls" leaf
Root.Sub.Add(TNode.Create('Recent Calls', -1));
// Add some recent calls
Root.Sub[1].Sub.Add(TNode.Create('+00 (000) 00.00.00', 3));
Root.Sub[1].Sub.Add(TNode.Create('+00 (001) 12.34.56', 4));

You need a recursive procedure to fill the virtual tree view using this type:

procedure TForm1.AddNodestoTree(ParentNode: PVirtualNode; Node: TNode);
var SubNode: TNode;
    ThisNode: PVirtualNode;

begin
  ThisNode := VT.AddChild(ParentNode, Node); // This call adds a new TVirtualNode to the VT, and saves "Node" as the payload

  Node.VTNode := ThisNode; // Save the PVirtualNode for future reference. This is only an example,
                           // the same TNode might be registered multiple times in the same VT,
                           // so it would be associated with multiple PVirtualNode's.

  for SubNode in Node.Sub do
    AddNodestoTree(ThisNode, SubNode);
end;

// And start processing like this:
VT.NodeDataSize := SizeOf(Pointer); // Make sure we specify the size of the node's payload.
                                    // A variable holding an object reference in Delphi is actually
                                    // a pointer, so the node needs enough space to hold 1 pointer.
AddNodesToTree(nil, Root);

When using objects, different nodes in your Virtual Tree may have different types of objects associated with them. In our example we're only adding nodes of TNode type, but in the real world you might have nodes of types TContact, TContactCategory, TRecentCall, all in one VT. You'll use the is operator to check the actual type of the object in the VT node like this:

procedure TForm1.VTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var PayloadObject:TObject;
    Node: TNode;
    Contact : TContact;      
    ContactCategory : TContactCategory;
begin
  PayloadObject := TObject(VT.GetNodeData(Node)^); // Extract the payload of the node as a TObject so
                                                   // we can check it's type before proceeding.
  if not Assigned(PayloadObject) then
    CellText := 'Bug: Node payload not assigned'
  else if PayloadObject is TNode then
    begin
      Node := TNode(PayloadObject); // We know it's a TNode, assign it to the proper var so we can easily work with it
      CellText := Node.Name;
    end
  else if PayloadObject is TContact then
    begin
      Contact := TContact(PayloadObject);
      CellText := Contact.FirstName + ' ' + Contact.LastName + ' (' + Contact.PhoneNumber + ')';
    end
  else if PayloadObject is TContactCategory then
    begin
      ContactCategory := TContactCategory(PayloadObject);
      CellText := ContactCategory.CategoryName + ' (' + IntToStr(ContactCategory.Contacts.Count) + ' contacts)';
    end
  else
    CellText := 'Bug: don''t know how to extract CellText from ' + PayloadObject.ClassName;
end;

And here's an example why to store VirtualNode pointer to your node instances:

procedure TForm1.ButtonModifyClick(Sender: TObject);
begin
  Root.Sub[0].Sub[0].Name := 'Someone else'; // I'll modify the node itself
  VT.InvalidateNode(Root.Sub[0].Sub[0].VTNode); // and invalidate the tree; when displayed again, it will
                                                // show the updated text.
end;

You know have an working example for a simple tree data structure. You'll need to "grow" this data structure to suite your needs: the possibilities are endless! To give you some ideas, directions to explore:

  • You can turn the Name:string into a virtual method GetText:string;virtual and then create specialized descendants of TNode that override GetText to provide specialized behavior.
  • Create a TNode.AddPath(Path:string; ID:Integer) that allows you to do Root.AddPath('Contacts\Abraham', 1); - that is, a method that automatically creates all intermediary nodes to the final node, to allow easy creation of the tree.
  • Include an PVirtualNode into TNode itself so you can check rather the Node is "checked" in the Virtual Tree. This would be a bridge of the data-GUI separation.
烛影斜 2024-11-01 03:56:12

我相信您最好找到一个包含通用树实现的现有库,然后您可以重新使用它来满足您的需求。

为了让您了解原因,这里是我编写的一些代码,用于说明对可以想象的最简单树结构的最简单操作。

type
  TNode = class
    Parent: TNode;
    NextSibling: TNode;
    FirstChild: TNode;
  end;

  TTree = class
    Root: TNode;
    function AddNode(Parent: TNode): TNode;
  end;

function TTree.AddNode(Parent: TNode);
var
  Node: TNode;
begin
  Result := TNode.Create;

  Result.Parent := Parent;
  Result.NextSibling := nil;
  Result.FirstChild := nil;

  //this may be the first node in the tree
  if not Assigned(Root) then begin
    Assert(not Assigned(Parent));
    Root := Result;
    exit;
  end;

  //this may be the first child of this parent
  if Assigned(Parent) and not Assigned(Parent.FirstChild) then begin
    Parent.FirstChild := Result;
  end;

  //find the previous sibling and assign its next sibling to the new node
  if Assigned(Parent) then begin
    Node := Parent.FirstChild;
  end else begin
    Node := Root;
  end;
  if Assigned(Node) then begin
    while Assigned(Node.NextSibling) do begin
      Node := Node.NextSibling;
    end;
    Node.NextSibling := Result;
  end;
end;

注意:我尚未测试此代码,因此无法保证其正确性。我预计它有缺陷。

这一切所做的只是向树添加一个新节点。它使您几乎无法控制节点添加到树中的位置。如果只是添加一个新节点作为指定父节点的最后一个兄弟节点。

要采用这种方法,您可能需要处理:

  • 在指定的同级之后插入。实际上这是上面的一个非常简单的变体。
  • 删除一个节点。这有点复杂。
  • 移动树中的现有节点。
  • 走树。
  • 将树连接到您的 VST。

这样做当然是可行的,但最好建议您找到已经实现该功能的第 3 方库。

I believe you will be best served by finding an existing library containing a general tree implementation which you can then re-use to serve your needs.

To give you an idea why, here is some code I wrote to illustrate the most simple operation on the most simple tree structure imaginable.

type
  TNode = class
    Parent: TNode;
    NextSibling: TNode;
    FirstChild: TNode;
  end;

  TTree = class
    Root: TNode;
    function AddNode(Parent: TNode): TNode;
  end;

function TTree.AddNode(Parent: TNode);
var
  Node: TNode;
begin
  Result := TNode.Create;

  Result.Parent := Parent;
  Result.NextSibling := nil;
  Result.FirstChild := nil;

  //this may be the first node in the tree
  if not Assigned(Root) then begin
    Assert(not Assigned(Parent));
    Root := Result;
    exit;
  end;

  //this may be the first child of this parent
  if Assigned(Parent) and not Assigned(Parent.FirstChild) then begin
    Parent.FirstChild := Result;
  end;

  //find the previous sibling and assign its next sibling to the new node
  if Assigned(Parent) then begin
    Node := Parent.FirstChild;
  end else begin
    Node := Root;
  end;
  if Assigned(Node) then begin
    while Assigned(Node.NextSibling) do begin
      Node := Node.NextSibling;
    end;
    Node.NextSibling := Result;
  end;
end;

Note: I have not tested this code and so cannot vouch for its correctness. I expect it has defects.

All this does is add an new node to the tree. It gives you little control over where in the tree the node is added. If simply adds a new node as the last sibling of a specified parent node.

To take this sort of approach you would likely need to deal with:

  • Inserting after a specified sibling. Actually this is quite a simple variant of the above.
  • Removing a node. This is somewhat more complex.
  • Moving existing nodes within the tree.
  • Walking the tree.
  • Connecting the tree to your VST.

It's certainly feasible to do this, but you may be better advised to find a 3rd party library that already implements the functionality.

甜扑 2024-11-01 03:56:12

我在这里问了类似的问题。我没有得到任何有用的答案,所以我决定自己实现,您可以找到在这里

编辑:
我将尝试发布如何使用我的数据结构的示例:

uses
  svCollections.GenericTrees;

声明您的数据类型:

type
  TMainData = record
    Name: string;
    ID: Integer;
  end;

在代码中的某处声明您的主数据树对象:

MyTree: TSVTree<TMainData>;

创建它(并且不要忘记稍后释放):

MyTree: TSVTree<TMainData>.Create(False);

将您的 VirtualStringTree 分配给我们的数据结构:

MyTree.VirtualTree := VST;

然后您可以使用一些值初始化您的数据树:

procedure TForm1.BuildStructure(Count: Integer);
var
  i, j: Integer;
  svNode, svNode2: TSVTreeNode<TMainData>;
  Data: TMainData;
begin
  MyTree.BeginUpdate;
  try
    for i := 0 to Count - 1 do
    begin
      Data.Name := Format('Root %D', [i]);
      Data.ID := i;
      svNode := MyTree.AddChild(nil, Data);
      for j:= 0 to 10 - 1 do
      begin
        Data.Name := Format('Child %D', [j]);
        Data.ID := j;
        svNode2 := MyTree.AddChild(svNode, Data);
      end;
    end;
  finally
    MyTree.EndUpdate;
  end;
end;

并设置 VST 事件来显示您的数据:

procedure TForm1.vt1InitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode;
  var ChildCount: Cardinal);
var
  svNode: TSVTreeNode<TMainData>;
begin
  svNode := MyTree.GetNode(Sender.GenerateIndex(Node));
  if Assigned(svNode) then
  begin
    ChildCount := svNode.FChildCount;
  end;
end;

procedure TForm1.vt1InitNode(Sender: TBaseVirtualTree; ParentNode,
  Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
var
  svNode: TSVTreeNode<TMainData>;
begin
  svNode := MyTree.GetNode(Sender.GenerateIndex(Node));
  if Assigned(svNode) then
  begin
    //if TSVTree<TTestas> is synced with Virtual Treeview and we are building tree by
    // setting RootNodeCount, then we must set svNode.FVirtualNode := Node to
    // have correct node references
    svNode.FVirtualNode := Node;  // Don't Forget!!!!
    if svNode.HasChildren then
    begin
      Include(InitialStates, ivsHasChildren);
    end;
  end;
end;

//display info how you like, I simply get name and ID values
procedure TForm1.vt1GetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var
  svNode: TSVTreeNode<TMainData>;
begin
  svNode := MyTree.GetNode(Sender.GenerateIndex(Node));
  if Assigned(svNode) then
  begin
    CellText := Format('%S ID:%D',[svNode.FValue.Name, svNode.FValue.ID]);
  end;
end;

此时,您仅使用 MyTree 数据结构,对其所做的所有更改都将反映在您分配的 VST 中。然后,您始终可以将底层结构保存(和加载)到流或文件中。希望这有帮助。

I asked similar question here. I didn't got any useful answers so I decide to make my own implementation, which you can find here.

EDIT:
I'll try to post example how you could use my data structure:

uses
  svCollections.GenericTrees;

Declare your data type:

type
  TMainData = record
    Name: string;
    ID: Integer;
  end;

Declare your main data tree object somewhere in your code:

MyTree: TSVTree<TMainData>;

Create it (and do not forget to free later):

MyTree: TSVTree<TMainData>.Create(False);

Assign your VirtualStringTree to our data structure:

MyTree.VirtualTree := VST;

Then you can init your data tree with some values:

procedure TForm1.BuildStructure(Count: Integer);
var
  i, j: Integer;
  svNode, svNode2: TSVTreeNode<TMainData>;
  Data: TMainData;
begin
  MyTree.BeginUpdate;
  try
    for i := 0 to Count - 1 do
    begin
      Data.Name := Format('Root %D', [i]);
      Data.ID := i;
      svNode := MyTree.AddChild(nil, Data);
      for j:= 0 to 10 - 1 do
      begin
        Data.Name := Format('Child %D', [j]);
        Data.ID := j;
        svNode2 := MyTree.AddChild(svNode, Data);
      end;
    end;
  finally
    MyTree.EndUpdate;
  end;
end;

And set VST events to display your data:

procedure TForm1.vt1InitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode;
  var ChildCount: Cardinal);
var
  svNode: TSVTreeNode<TMainData>;
begin
  svNode := MyTree.GetNode(Sender.GenerateIndex(Node));
  if Assigned(svNode) then
  begin
    ChildCount := svNode.FChildCount;
  end;
end;

procedure TForm1.vt1InitNode(Sender: TBaseVirtualTree; ParentNode,
  Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
var
  svNode: TSVTreeNode<TMainData>;
begin
  svNode := MyTree.GetNode(Sender.GenerateIndex(Node));
  if Assigned(svNode) then
  begin
    //if TSVTree<TTestas> is synced with Virtual Treeview and we are building tree by
    // setting RootNodeCount, then we must set svNode.FVirtualNode := Node to
    // have correct node references
    svNode.FVirtualNode := Node;  // Don't Forget!!!!
    if svNode.HasChildren then
    begin
      Include(InitialStates, ivsHasChildren);
    end;
  end;
end;

//display info how you like, I simply get name and ID values
procedure TForm1.vt1GetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var
  svNode: TSVTreeNode<TMainData>;
begin
  svNode := MyTree.GetNode(Sender.GenerateIndex(Node));
  if Assigned(svNode) then
  begin
    CellText := Format('%S ID:%D',[svNode.FValue.Name, svNode.FValue.ID]);
  end;
end;

At this point you work only with your MyTree data structure and all the changes made to it will be reflected in your assigned VST. You then can always save (and load) underlying structure to stream or file. Hope this helps.

咋地 2024-11-01 03:56:12

如果我理解正确的话,你的树需要一个数据结构。每个单独的节点都需要一条记录来保存其数据。但底层的等级制度可以通过几种不同的方式进行管理。我猜这一切都需要在某种数据库中进行管理 - 这已经在本网站上讨论过了,所以我将向您指出:

在数据库中实现分层数据结构

,此处:

将平面表解析为树的最有效/优雅的方法是什么?< /a>

和此处:

SQL - 如何存储和导航层次结构?

嵌套集模型:

http://mikehillyer.com/articles/managing- mysql 中的分层数据/

If I understand correctly, you need a datastructure for your tree. Each individual node requires a record to hold its data. But the underlying heirarchy can be managed in a few different ways. Im guessing this is all to be managed in some sort of database - This has already been talked about on this site, so i will point you to:

Implementing a hierarchical data structure in a database

and here:

What is the most efficient/elegant way to parse a flat table into a tree?

and here:

SQL - How to store and navigate hierarchies?

Nested Set Model:

http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

西瑶 2024-11-01 03:56:12

如果您使用的是支持泛型的最新版本的 Delphi,请检查 GenericTree

If you are using recent versions of Delphi that supports Generics, check GenericTree

£冰雨忧蓝° 2024-11-01 03:56:12

现在 Delphi 有泛型。我刚刚发明了一个非常好的树数据结构。暂时不会放弃代码,不是真正的开源人士,也许在不久的将来,还有其他原因请参见下文。

但我会给出一些关于如何重新创建它的提示:

假设所有节点都可以包含相同的数据结构(从上面看来就是这种情况,一个字符串,一个 id,然后是链接。

您需要重新创建的成分) -create 这是以下内容:

  1. 泛型
  2. A 泛型类型 T
  3. 该类型 T 需要限制为类和构造函数,如下所示:

(在您的情况下,将类替换为记录,未经测试,但也可能有效)

  1. 两个字段:self的节点数组(提示hint),数据:T;

  2. 属性

  3. 不仅仅是任何属性,而是默认属性;)

  4. 吸气剂。

  5. 具有深度和子级的递归构造函数。

  6. 一些 if 语句来停止构造。

  7. 当然还有 SetLength 来创建链接/节点,并在 for 循环中调用一些创建,然后进行一些减法;)

给定你们所有人足够的提示,看看是否有人可以重新创建它会很有趣,否则我可能会还不如申请专利,只是开玩笑,不会砸钱反对它,尽管有其他设施,但可能会扩大课程范围。

该类在构造过程中像真正的数据结构一样分配所有节点......注意添加和删除等,至少现在不是。

现在是这个(秘密)设计最有趣和有趣的方面,我有点想要的东西现在已经成为现实。我现在可以编写如下代码:

TGroup 只是一个示例,可以是任何东西,只要它在我的例子中是一个类即可。
在本例中,它只是一个带有 mString 的类。

var
  mGroupTree : TTree<TGroup>;

procedure Main;
var
  Depth : integer;
  Childs : integer;
begin

  Depth := 2;
  Childs := 3;

  mGroupTree := TTree<TGroup>.Create( Depth, Childs );

  mGroupTree.Data.mString := 'Basket'; // notice how nice this root is ! ;)

  mGroupTree[0].Data.mString := 'Apples';
  mGroupTree[1].Data.mString := 'Oranges';
  mGroupTree[2].Data.mString := 'Bananas';

  mGroupTree[0][0].Data.mString := 'Bad apple';
  mGroupTree[0][1].Data.mString := 'Average apple';
  mGroupTree[0][2].Data.mString := 'Good apple';

  mGroupTree[1][0].Data.mString := 'Tiny orange';
  mGroupTree[1][1].Data.mString := 'Medium orange';
  mGroupTree[1][2].Data.mString := 'Big orange';

  mGroupTree[2][0].Data.mString := 'Straight banana';
  mGroupTree[2][1].Data.mString := 'Curved banana';
  mGroupTree[2][2].Data.mString := 'Crooked banana';

现在您可能会从这个实际测试代码中注意到,它允许“数组扩展”,就像我很少看到的那样,这要归功于这个属性,它的自引用有点像...

所以[][]是深度2。
[][][] 的深度为 3。

我仍在评估它的用途。

一个潜在的问题是 Delphi 没有真正的技术来自动扩展这些数组,尽管我还没有找到并且对此感到满意。

我想要一种技术,我可以编写一些可以进入任何深度级别的代码:

[0][0][0][0][0]

还不确定如何做到这一点...简单的选项是“递归” 。

真实示例:

procedure DisplayString( Depth : string; ParaTree : TTree<TGroup>);
var
  vIndex : integer;
begin
  if ParaTree <> nil then
  begin
//    if ParaTree.Data.mString <> '' then
    begin
      writeln( ParaTree.Data.mString );

      Depth := Depth + ' ';
      for vIndex := 0 to ParaTree.Childs-1 do
      begin
        DisplayString( Depth, ParaTree[vIndex] );
      end;
    end;
  end;
end;

有点有趣不是吗。

仍在探索它对于“实际应用程序”的有用性以及我是否想使用递归;)

也许有一天我会开源我的所有代码。我已经快40岁了,当我过了40岁,从39岁到40岁,我有点打算开源。距离 40 =D 还差 4 个月

(我必须说这是我第一次对泛型印象深刻,很久以前就测试过它,当时它是超级错误的,也许设计方面无法使用,但现在错误已修复并限制了泛型,它在 2018 年 8 月最新的 Delphi Toyko 10.2.3 版本中非常令人印象深刻!;) :))

我只是触及了最新 Delphi 技术不可能实现的表面,也许使用匿名方法编写递归例程来处理此数据结构可能会成为一个问题。更容易一些,也许也可以考虑并行处理,Delphi 帮助提到了匿名方法的这一点。

再见,
斯凯巴克。

Delphi has generics nowadays. I just invented a very nice tree data structure. Not gonna give code away just yet, not really an open source person, maybe in the near future though, also other reasons see below.

But I will give some hints on how to re-create it:

Assuming all your nodes can contain the same data structure (which seems to be the case from above, a string, an id, and then links.

The ingredients you need to re-create this is the following:

  1. Generics
  2. A generic type T
  3. This type T needs to be constrained to class and constructor as follows:

<T : class, constructor> (In your case replace class with record, untested, but may also work)

  1. two fields: node array of self (hint hint), data : T;

  2. A property

  3. Not just any propery, a default property ;)

  4. A getter.

  5. A recursive constructor with depth and child.

  6. Some if statement to stop the construction.

  7. And ofcourse SetLength to create the links/nodes and calling some creates in a for loop and then some subtraction of something ;)

Given all of you enough hints, would be fun and interesting to see if anybody can re-create it, otherwise I might just as well patent it, just kidding, not gonna throw money against it, might expand the class though with other facilities.

The class allocates all nodes during construction like a true data structure... noting with add and remove and such, at least not for now.

Now comes the most interesting and funny aspect of this (secret) design, something I kinda wanted and is now a reality. I can now write code as follows:

TGroup is just an example can be anything as long as it's a class in my case.
In this case it's just a class with mString

var
  mGroupTree : TTree<TGroup>;

procedure Main;
var
  Depth : integer;
  Childs : integer;
begin

  Depth := 2;
  Childs := 3;

  mGroupTree := TTree<TGroup>.Create( Depth, Childs );

  mGroupTree.Data.mString := 'Basket'; // notice how nice this root is ! ;)

  mGroupTree[0].Data.mString := 'Apples';
  mGroupTree[1].Data.mString := 'Oranges';
  mGroupTree[2].Data.mString := 'Bananas';

  mGroupTree[0][0].Data.mString := 'Bad apple';
  mGroupTree[0][1].Data.mString := 'Average apple';
  mGroupTree[0][2].Data.mString := 'Good apple';

  mGroupTree[1][0].Data.mString := 'Tiny orange';
  mGroupTree[1][1].Data.mString := 'Medium orange';
  mGroupTree[1][2].Data.mString := 'Big orange';

  mGroupTree[2][0].Data.mString := 'Straight banana';
  mGroupTree[2][1].Data.mString := 'Curved banana';
  mGroupTree[2][2].Data.mString := 'Crooked banana';

Now what you may notice from this actual test code is that it allows "array expansion" like I have rarely seen thanks to this property, which self-references sort of...

So [] [] is depth 2.
[][][] would be depth 3.

I am still evaluating the use of this.

One potential problem is Delphi has no real technique to auto-expand these arrays, though none I have yet found and are statisfied with.

I would like a technique where I can write some code which can go to any depth level:

[0][0][0][0][0]

Not yet sure how to do that... simpelst option is "recursion".

real example:

procedure DisplayString( Depth : string; ParaTree : TTree<TGroup>);
var
  vIndex : integer;
begin
  if ParaTree <> nil then
  begin
//    if ParaTree.Data.mString <> '' then
    begin
      writeln( ParaTree.Data.mString );

      Depth := Depth + ' ';
      for vIndex := 0 to ParaTree.Childs-1 do
      begin
        DisplayString( Depth, ParaTree[vIndex] );
      end;
    end;
  end;
end;

Kinda interesting isn't it.

Still exploring it's usefullness for "real applications" and if I want to go with recursion or not ;)

Maybe some day I will open source all of my code. I am close to 40 years old, when I go beyond 40, from 39 to 40, I was kinda planning on going open source. Still 4 months away from 40 =D

(I must say this is the first time I am impressed by Generics, tested it long ago, it was super buggy back then and maybe design-wise unusable, but now with the bugs fixed and constrained generics, it's very impressive in latest Delphi Toyko 10.2.3 version august 2018 ! ;) :))

I am just scratching the surface of what is impossible with latest Delphi tech, maybe with anonymous methods writing recursive routines to process this data structure might become a bit easier, also maybe parallel processing might come into consideration, Delphi help mentions this for anonymous methods.

Bye,
Skybuck.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文