在C#中读取递归集合

时间:2014-01-21 23:42:06

标签: c# recursion linked-list

我正在开发一个从Beckhoff PLC读取值的应用程序。 plc有一个.net库,我可以用它来连接我的程序和PLC。

PLC上的每个变量都是TcAdsSymbolInfo类型的符号。这个类有几个成员,其中一个是TcAdsSymbolInfo的集合等等......你可以看到它的发展方向。基本上我有一个树形结构,有许多符号,每个符号可以有子符号。 Beckhoff

我想要做的是,对于每个符号读取所有子符号,如果我手工完成并尝试查看第一个子符号级别,这是有效的,但我真的需要所有这些。

我试图创建一个递归函数,它接收一个TcAdsSymbolInfo并在最后调用它自己,但这会抛出一个Stack Overflow异常

    private void ReadSubsymbols(TcAdsSymbolInfo t)
    {
        if (t.SubSymbolCount > 0)
        {
            foreach (TcAdsSymbolInfo subsymbol in t.SubSymbols)
            {
                if (!symbols.ContainsKey(subsymbol.Name))
                    symbols.Add(subsymbol.Name, subsymbol);
                try
                {
                    ReadSubsymbols(subsymbol);
                }
                catch (Exception Ex)
                {
                    Console.WriteLine(Ex.ToString() + " - " + Ex.Message);
                }
            }
        }
    }

符号只是一个字典,我存储符号名称和符号本身。

请尝试从PLC部分摘要,因为我认为这只是一个纯粹的逻辑/编程问题。在与PLC通信或读写值时没有任何问题。唯一的问题是阅读该结构。

我可以使用一段时间或任何其他类型的循环对此进行任何分析吗?什么都没有抛出异常?

提前致谢。

3 个答案:

答案 0 :(得分:2)

您可以使用迭代树遍历。 但在尝试此之前,请确保堆栈溢出是由于递归变得太深。它很可能是一个编码错误,允许递归无限下降。

您可以执行以下Psuedo代码:

Push root node to stack

While (stack is not empty)
{
    current node = pop from stack
    process current node (and other processing goes here)

    add all children with nodes to stack
}

请参阅此示例以解决与目录类似的问题: http://msdn.microsoft.com/en-us/library/bb513869.aspx

答案 1 :(得分:1)

这是一个简单的树步行。基本逻辑是从根开始:

  • 如果root为null,则树为空:我们已完成。
  • 如果root为非null,
    • 访问它
    • 然后递归访问每个孩子。

易。主要是:D你遇到麻烦的地方是你在图中遇到周期时(例如,当一个子节点链接回一个自己的父节点时)。如果你有周期,你必须跟踪你访问过的节点(就像我的例子那样),并在你去的时候检查周期(我的例子没有)。

鉴于这样的课程:

class SymbolInfo
{
  public string Name { get ; set ; }
  public SortedSet<SymbolInfo> Subsymbols { get ; set ; } 

  public SymbolInfo( string name )
  {
    this.Name = name ;
    this.Subsymbols = new SortedSet<SymbolInfo>( new SymbolInfo.Comparer() ) ;
  }

  public override string ToString()
  {
    return this.Name ?? "-null-" ;
  }

  private class Comparer : IComparer<SymbolInfo>
  {
    public int Compare( SymbolInfo x , SymbolInfo y )
    {
      return string.Compare(x.Name,y.Name,StringComparison.InvariantCultureIgnoreCase) ;
    }
  }
}

树步行看起来像这样:

public static IEnumerable<string> TreeWalk( SymbolInfo root , List<SymbolInfo> visited )
{
  if ( root != null )
  {
    visited.Add(root) ;
    yield return string.Join( " -> " , visited ) ;
    foreach ( SymbolInfo child in root.Subsymbols )
    {
      foreach ( string childPath in TreeWalk( child , visited ) )
      {
        yield return childPath ;
      }
    }
    visited.RemoveAt(visited.Count-1) ;
  }
}

给出一个这样构造的树:

private static SymbolInfo LoadTree()
{
  SymbolInfo a = new SymbolInfo("A") ;
  SymbolInfo b = new SymbolInfo("B") ;
  SymbolInfo c = new SymbolInfo("C") ;
  SymbolInfo d = new SymbolInfo("D") ;
  SymbolInfo e = new SymbolInfo("E") ;
  SymbolInfo f = new SymbolInfo("F") ;
  SymbolInfo g = new SymbolInfo("G") ;
  SymbolInfo h = new SymbolInfo("H") ;
  SymbolInfo i = new SymbolInfo("I") ;

  a.Subsymbols.Add(b) ;
  a.Subsymbols.Add(c) ;
  a.Subsymbols.Add(d) ;

  b.Subsymbols.Add(e) ;

  c.Subsymbols.Add(f) ;
  c.Subsymbols.Add(g) ;

  f.Subsymbols.Add(h) ;
  f.Subsymbols.Add(i) ;

  return a ;
}

我们可以像这样调用它:

SymbolInfo root = LoadTree() ;

foreach ( string path in TreeWalk( root , new List<SymbolInfo>() ) )
{
  Console.WriteLine(path) ;
}

产生以下输出:

A
A -> B
A -> B -> E
A -> C
A -> C -> F
A -> C -> F -> H
A -> C -> F -> I
A -> C -> G
A -> D

答案 2 :(得分:0)

public IEnumerable<TcAdsSymbolInfo> GetSymbols(IEnumerable<TcAdsSymbolInfo> set)
{
    if (!set.Any())
        return Enumerable.Empty<TcAdsSymbolInfo>();

    return set.SelectMany(sym => GetSymbols(sym.SubSymbols)));
}

然后,您可以将结果转换为字典并检查欺骗等。