简体   繁体   English

在C#,C ++和Java中创建python弱类型结构的强类型版本

[英]Creating in c#,c++ and java a strong typed version of a python weak typed structure

In python I have the following: 在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]

this is a structure to represent a graph and that I find nice because its structure is the same as the one of one of it's nodes so I can use it directly to initiate a search (as in depth-first). 这是一种表示图的结构,我发现很好,因为它的结构与其节点之一相同,因此我可以直接使用它来发起搜索(如深度优先)。 The printed version of it is: 它的印刷版本是:

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

And it can be used like: 它可以像这样使用:

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

Now, I'm curious to know how would one implement it in C++, C# and Java without resorting to "Object" tricks that would fill the code with ugly casts. 现在,我很好奇知道如何在不使用“对象”技巧的情况下用C ++,C#和Java来实现它,而这些技巧将用丑陋的演员表填充代码。 For C++ I was thinking in templatemeta programming but that would generate "finite data types" when what is needed is something like 对于C ++,我一直在考虑templatemeta编程,但是当需要的东西像这样时,它将生成“有限数据类型”

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

In Java, I would go with a Node class which represents any node of a graph. 在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;
    }
}

If you want a graph that will contain int values you can instantiate it and use it with: 如果您想要一个包含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

For storing either further graphs or "terminal" values (actually, both of these approaches generalize to arbitarily many types with any interpretation, as long as they can be enumerated at compiletime), you use either: 为了存储其他图形或“终端”值(实际上,只要可以在编译时将它们枚举,这两种方法都可以概括为任意类型的任意类型的任意解释):

  • Unions (possibly discriminated ), or 工会 (可能有歧视 ),或
  • Polymorphism (specifically, subtype polymorphism) 多态性(特别是亚型多态性)

In either case, you have a type Graph behind which you can hide both nested graphs and stored values. 在任何一种情况下,您都有一个Graph类型,可以在其后隐藏嵌套的图形和存储的值。

In C++ specifically, you'd probably use the former a union or Boost::Variant (more typesafe and convenient to handle). 具体来说,在C ++中,您可能会使用前者的unionBoost::Variant (类型安全且易于处理)。 You may need to forward-declare the class so it's visible at the time you define it. 您可能需要向前声明该类,以便在定义它时可以看到它。 A union offers enough place to store one value of either type, and (either implicitly in Boost::Variant or explicitly with plain old union ) a "tag" indicating which one is the case. 联合提供了足够的位置来存储任一类型的值,并且(可以在Boost::Variant隐式地使用普通的旧union显式地存储)一个“标签”,以指示哪种情况。 You can then look at any stored value and tell if it's another graph or a terminal value, and extract the associated value. 然后,您可以查看任何存储的值,并判断它是另一个图形还是终端值,然后提取关联的值。

In Java and C#, you don't have that much support for straight-forward union types, so you'd use the second option. 在Java和C#中,您对直接联合类型没有太多的支持,因此可以使用第二种选择。 There's an interface (or abstract) class IGraph , with one implementation for graphs (refering to IGraph s) and one for wrapping non-graph values in another subtype of IGraph . 有一个接口(或抽象)类IGraph ,其中一个用于图形的实现(指IGraph ),一个用于将非图形值包装在另一个IGraph子类型中。 Basically you use subtype polymorphism. 基本上,您使用子类型多态性。 This is possible in C++ too, but I get the impression that a union is a better idea if the possible types are known beforehand, unlikely to ever change, and small in number. 这在C ++中也是可能的,但是我给人的印象是,如果事先知道可能的类型,不可能改变并且数量很少,那么联合是一个更好的主意。 It also allows you to avoid some pointers/references - both union s and Boost::Variant s can be stored on the stack, while polymorphism requires indirection. 它还允许您避免使用某些指针/引用unionBoost::Variant都可以存储在堆栈中,而多态性则需要间接的。

Here's a simple hack: 这是一个简单的技巧:

#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];
  }
};

We add some ostream overloads for printing: 我们为打印添加了一些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;
}

Example usage: 用法示例:

#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;
}

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

Further features are easily added; 其他功能很容易添加; at the moment it's not clear to me whether you want all nodes to also support satellite data ("values"), or whether only leaf nodes can have values, or if there isn't any additional data. 目前,尚不清楚我是否要所有节点也都支持卫星数据(“值”),或者是否只有叶节点可以具有值,或者是否没有任何其他数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM