【问题标题】:How to Populate a 'Tree' structure 'Declaratively'如何“以声明方式”填充“树”结构
【发布时间】:2012-11-23 15:16:35
【问题描述】:

我想定义一个“节点”类/结构,然后在代码中声明这些节点的树,这样代码的格式化方式反映了树结构,并且没有“太多”样板路。

请注意,这不是关于数据结构的问题,而是关于我可以使用 C++ 的哪些特性来获得与以下示例类似的声明性代码风格。

可能使用 C++0X 这会更容易,因为它在构造对象和集合方面具有更多功能,但我使用的是 Visual Studio 2008。

示例树节点类型:

struct node
{ 
  string name;
  node* children;

  node(const char* name, node* children);
  node(const char* name);
};

我想做的事:

声明一棵树,使其结构反映在源代码中

node root =
  node("foo",
  [
    node("child1"),
    node("child2", 
    [
      node("grand_child1"),
      node("grand_child2"),
      node("grand_child3"
    ]),
    node("child3")
  ]);

注意:我不想做的事情:

声明一大堆临时对象/colls 并“向后”构造树

node grandkids[] = node[3] 
{
  node("grand_child1"),
  node("grand_child2"),
  node("grand_child3"
};

node kids[] = node[3]
{
  node("child1"),
  node("child2", grandkids) 
  node("child3")
};

node root = node("foo", kids);

【问题讨论】:

  • 在我看来像是表达式模板的案例。
  • 有人会认为root("foo").child("child1).silbing("child2").child("grand_child1").silbing("grand_child2").grand_silbing("grand_child3").parent().silbing("child3") 可以吗?这可以通过fluent interfaces 来完成,如果您愿意,可以添加一些换行符;)
  • 流畅的接口想法确实出现了,但我更喜欢“噪音”较小的东西 - 如果我可以将数组文字(子节点列表)作为参数传递给函数(节点构造函数)那么我相信这已经足够了,但我认为这是不可能的。
  • 我什至愿意使用聪明的预处理器宏...
  • VS 2008 是否支持统一初始化?我没有足够关注他们的编译器。

标签: c++ visual-studio-2008 data-structures tree


【解决方案1】:

如果您不介意过度复制节点并使用括号() 而不是方括号[],那么这应该可以。

实际上你可以通过将指针存储在node_group而不是副本中来避免复制,但是由于这是星期五下午而且我很懒,所以我将它留给你。

struct node
{
    std::string name;
    std::vector<node> children;

    node(const char* n)
        :   name (n)
    {
    }

    node(const char* n, const class node_group& group);
};

struct node_group
{
    std::vector<node> children;
};

node::node(const char* n, const class node_group& group)
    :   name (n)
    ,   children (group.children)
{
}

node_group operator ,(const node& n1, const node& n2)
{
    node_group group;
    group.children.push_back (n1);
    group.children.push_back (n2);
    return group;
}

node_group operator ,(const node_group& gr, const node& n2)
{
    node_group group (gr);
    group.children.push_back (n2);
    return group;
}


int main ()
{
    node root ("foo",
                (node("child1"),
                node("child2",
                    (node("grand_child1"),
                    node("grand_child2"),
                    node("grand_child3"))
                    ),
                node("child3"))
              );
}

【讨论】:

    【解决方案2】:

    http://en.wikipedia.org/wiki/Expression_templates

    使用这种技术,您可以获得如下语法:

    Node root("bob") =
      (node("child1")
        <<(
          node("grandkid"),
          node("grandkid2")
        )
      ),
      node("child2");
    

    在哪里重载 operator,operator&lt;&lt; 以构建用于构建根节点的表达式树。

    【讨论】:

      【解决方案3】:

      为您想要使用的语法编写解析器并不难。这是一个简单但有效的代码。我让自己稍微改变你的 node 对象,它不使用指针数组来存储孩子,而是 std::vector

      这种方法的优点是您可以从运行时提供的文本构建树,例如从配置文件中读取。另请注意,ostream&amp; operator&lt;&lt;(ostream&amp;, const node&amp;) 以相同的格式打印您的树,这对于序列化您的树(将它们写入文件或通过网络发送)或单元测试非常方便。

          #include <stdio.h>
          #include <stdlib.h>
          #include <ctype.h>
          #include <string>
          #include <vector>
          #include <iostream>
      
      
          using namespace std;
      
          struct node
          { 
            string name;
            vector<node*> children;
      
            node(const string& name, const vector<node*> children);
            node(const string& name);
          };
      
          ostream& operator<<(ostream& o, const node& n) {
              o << "node('" << n.name << "'";
              if (n.children.size()) {
                  o << ", [";
                  for (size_t i = 0; i < n.children.size(); ++i)
                      o << (i ? "," : "") << *(n.children[i]);
                  o << "]";
              }
              o << ")";
              return o;
          }
      
          node::node(const string& s, const vector<node*> children)
          : name(s), children(children) {}
      
          char* parseNode(node** n, const char *ss);
      
          char *skipSpace(const char *ss) {
              char *s = (char *) ss;
              while (isspace(*s))
                  ++s;
              return s;
          }
      
          void expected_error(const char* s) { 
              fprintf(stderr, "Error: expected '%s'\n", s);
              exit(1);
          }
      
          char *expect(const char *expected, char *s) {
              char *ex = skipSpace(expected);
              s = skipSpace(s);
              for ( ; *ex && *s; ++ex, ++s)
                  if (*ex != *s) expected_error(expected);
              if (*ex) expected_error(expected);
              return s;
          }
      
          char *expectString(string& str, const char *ss)
          {
              char *s = skipSpace(ss);
              s = expect("'", s);
              char *start = s++;
              while (*s != '\'')
                  ++s;
              str = string(start, s - start);
              return ++s;
          }
      
          char * parseChildren(vector<node*>& children, char *s) {
              s = expect("[", s);
              s = skipSpace(s);
              while (*s != ']') {
                  node *n = 0;
                  s = parseNode(&n, s);
                  children.push_back(n);
                  s = skipSpace(s);
                  if (*s == ',')
                      ++s;
                  else break;
              }
              s = expect("]", s);
              return s;
          }
      
          char* parseNode(node** n, const char *ss) {
              char *s = (char *) ss;
              s = expect("node", s);
              s = expect("(", s);
              string name;
              s = expectString(name, s);
              vector<node*> children;
              s = skipSpace(s);
              if (*s == ',') 
                  s = parseChildren(children, ++s);
              *n = new node(name, children);
              s = expect(")", s);
              return s;
          }
      
          int main()
          {
              node * n = 0;
              parseNode(&n,
          " node('foo',"
          "  ["
          "    node('child1'),"
          "    node('child2', "
          "    ["
          "     node('grand_child1'),"
          "     node('grand_child2'),"
          "     node('grand_child3')"
          "    ]),"
          "   node('child3')"
          " ])"
          );
              cout << *n;
              return 0;
          }
      

      【讨论】:

      • 我确实考虑过使用 JSON 作为一种表达树结构的方式,以及一些从 JSON 构造对象树的代码,因为我可以访问 JSON 解析器。
      猜你喜欢
      • 2012-11-21
      • 1970-01-01
      • 2011-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-11
      • 2015-04-25
      • 1970-01-01
      相关资源
      最近更新 更多