【问题标题】:Creating in c#,c++ and java a strong typed version of a python weak typed structure在 c#、c++ 和 java 中创建 python 弱类型结构的强类型版本
【发布时间】:2012-01-19 16:01:53
【问题描述】:

在 python 中,我有以下内容:

graph = {}

graph[1] = {}
graph[2] = {}
graph[3] = {}

graph[1][3] = graph[3]

graph[2][1] = graph[1]
graph[2][3] = graph[3]

graph[3][2] = graph[2]

这是一个表示图形的结构,我觉得它很好,因为它的结构与其中一个节点相同,因此我可以直接使用它来启动搜索(如深度优先)。它的印刷版是:

{1: {3: {2: {1: {...}, 3: {...}}}}, 2: {1: {3: {2: {...}}}, 3: {2: {...}}}, 3: {
2: {1: {3: {...}}, 3: {...}}}}

它可以像这样使用:

graph[1][3][2][3][2][1][3][2][1][3][2].keys()

现在,我很想知道如何在 C++、C# 和 Java 中实现它,而无需诉诸“对象”技巧,这些技巧会使代码充满丑陋的演员表。对于 C++,我在考虑模板元编程,但是当需要类似的东西时会生成“有限数据类型”

map<int,map<int,...>> or map<int,...>

【问题讨论】:

标签: c# java c++ python strong-typing


【解决方案1】:

为了存储更多的图形或“终端”值(实际上,这两种方法都可以泛化为具有任何解释的任意多种类型,只要它们可以在编译时枚举),您可以使用:

在任何一种情况下,您都有一个 Graph 类型,您可以在其后面隐藏嵌套图和存储值。

特别是在 C++ 中,您可能会使用前者 unionBoost::Variant(更安全且易于处理)。您可能需要前向声明该类,以便在您定义它时可见。联合提供了足够的位置来存储任一类型的值,并且(在 Boost::Variant 中隐含或在普通的旧 union 中显式)一个“标签”指示是哪种情况。然后,您可以查看任何存储的值并判断它是另一个图形还是终端值,并提取相关值。

在 Java 和 C# 中,您对直接联合类型没有太多支持,因此您将使用第二个选项。有一个接口(或抽象)类IGraph,一个用于图形的实现(参考IGraphs),一个用于将非图形值包装在IGraph 的另一个子类型中。基本上你使用子类型多态性。这在 C++ 中也是可能的,但我的印象是,如果事先知道可能的类型、不太可能改变并且数量很少,联合是一个更好的主意。它还允许您避免一些指针/引用 - unions 和 Boost::Variants 都可以存储在堆栈中,而多态性需要间接。

【讨论】:

    【解决方案2】:

    在 Java 中,我会使用 Node 类,它代表图的任何节点。

    public class Node<T> {
        private List<Node<T>> children = new ArrayList<Node<T>>();
        private T value;
    
        public Node(T value) {
            this.value = value;
        }
    
        public void addChild(Node<T> child) {
            this.children.add(child);
        }
    
        public Node<T> getChild(int index) {
            return this.children.get(index);
        }
    
        public List<Node<T>> getChildren() {
            return this.children;
        }
    
        public T getValue() {
            return this.value;
        }
    }
    

    如果您想要一个包含 int 值的图表,您可以实例化它并将其用于:

    Node<Integer> graph = new Node<Integer>(10); //value of the first node is 10
    graph.addChild(new Node<Integer>(-3));
    graph.getChild(0).addChild(new Node<Integer>(5));
    System.out.println(graph.getChild(0).getChild(0).getValue()); //prints 5
    

    【讨论】:

    • 如果你可以用Java覆盖operator[],你实际上可以获得与上面基本相同的语法。但这是运算符重载危险的一个很好的例子:这里的getChild() 方法导致代码更清晰恕我直言。
    【解决方案3】:

    这是一个简单的技巧:

    #include <map>
    #include <memory>
    
    struct GraphNode
    {
      std::map<std::size_t, std::unique_ptr<GraphNode>> m;
    
      GraphNode & operator[](std::size_t i)
      {
        if (!m[i]) { m[i].reset(new GraphNode); }
        return *m[i];
      }
    };
    

    我们为打印添加了一些 ostream 重载:

    #include <prettyprint.hpp>
    
    std::ostream & operator<<(std::ostream & o, GraphNode const & g)
    {
      if (g.m.empty()) return o << "*";
      return o << g.m;
    }
    std::ostream & operator<<(std::ostream & o, std::unique_ptr<GraphNode> const & p)
    {
      if (!p) return o << "*";
      return o << *p;
    }
    

    示例用法:

    #include <iostream>
    
    int main()
    {
      GraphNode n;
    
      n[2] = GraphNode();
      n[4] = GraphNode();
    
      n[2][3] = GraphNode();
      n[2][8] = GraphNode();
      n[2][1] = GraphNode();
    
      std::cout << n << std::endl;
    }
    

    打印:[(2, [(1, *), (3, *), (8, *)]), (4, *)]

    其他功能很容易添加;目前我不清楚您是否希望所有节点也支持卫星数据(“值”),或者是否只有叶节点可以具有值,或者是否没有任何其他数据。

    【讨论】:

      猜你喜欢
      • 2020-02-19
      • 1970-01-01
      • 2011-08-02
      • 1970-01-01
      • 1970-01-01
      • 2013-02-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多