【问题标题】:C# parsing custom hierarchical expression - build treeC#解析自定义层次表达式——构建树
【发布时间】:2014-05-16 11:19:58
【问题描述】:

我正在构建由非常自定义的语言驱动的自定义资源提供程序。为此,我必须从自定义表达式创建树数据结构。让我解释一下:

f1(f2,f3,f6(f4),f5)

上面是我的自定义表达式的示例,我想从中构建树。根 - f1,有孩子:f2f3f4f5。但是f4 也有自己的孩子。

我已经为这个问题编写了解决方案,但我想找到更好的方法来实现这个目标。

class Node
{
    public string val;
    public List<Node> child = new List<Node>();
}

private Node parseInput(string input, int index)
{
    string nodeName = findToken(input,ref index);
    Node tmp = new Node() { val = nodeName };
    tmp.child = expandNodes(input, ref index);
    return tmp;
}


private List<Node> expandNodes(string input, ref int index)
{
    List<Node> res = new List<Node>();
    while (!char.IsLetterOrDigit(input[index++]) && index < input.Length) ;
    index--;

    while (index < input.Length)
    {
        if (checkNext(input, index, ')'))
        {
            while (!char.IsLetterOrDigit(input[index++]) && index < input.Length) ;
            index--;
            return res;
        }
        Node tmp = new Node() { val = findToken(input,ref index) };
        if (checkNext(input, index, '('))
        {
            tmp.child = expandNodes(input, ref index);
        }
        res.Add(tmp);
    }


    return res;
}


private bool checkNext(string s, int index, char desiredChar)
{
    string vc = "" + s[index];
    while (index < s.Length && !char.IsLetterOrDigit(s[index]))
    {
        char chr = s[index];
        if (chr == desiredChar)
        {
            return true;
        }
        index++;
    }
    return false;
}


private string findToken(string s, ref int index)
{
    string res = null;
    while (!char.IsLetterOrDigit(s[index++]) && index < s.Length) ;
    index--;

    while (index < s.Length && char.IsLetterOrDigit(s[index]))
    {
        res += s[index];
        index++;
    }
    return res;
}

【问题讨论】:

  • 你在寻找什么样的“更好的方法”?
  • 更高效、更优雅的算法

标签: c# parsing expression hierarchical


【解决方案1】:

我宁愿把Parse作为一个静态方法放到Node类中:

    // Simplified implementation: some checks removed, many useful methods omitted
    public class Node {
      private String m_Title;
      private Node m_Parent;
      private List<Node> m_Items = new List<Node>();

      public Node(String title) {
        m_Title = title;
      }

      public Node Parent {
        get {
          return m_Parent;
        }
      }

      public IReadOnlyList<Node> Items {
        get {
          return m_Items;
        }
      }

      public void Add(Node value) {
        m_Items.Add(value);

        value.m_Parent = this;
      }

      public String Title {
        get {
          return m_Title;
        }
      }

      public override String ToString() {
        if (m_Items.Count <= 0)
          return m_Title;

        StringBuilder Sb = new StringBuilder();

        Sb.Append(m_Title);

        Sb.Append("(");

        for (int i = 0; i < m_Items.Count; ++i) {
          Sb.Append(m_Items[i].ToString());

          if (i < m_Items.Count - 1)
            Sb.Append(",");
        }

        Sb.Append(")");

        return Sb.ToString();
      }

      public static Node Parse(String value) {
        Node owner = null; 
        Node current = null;

        StringBuilder Sb = new StringBuilder();

        foreach (Char ch in value) {
          if (Char.IsLetterOrDigit(ch))
            Sb.Append(ch);
          else {
            if (Sb.Length > 0) {
              current = new Node(Sb.ToString());

              Sb.Length = 0;

              if (owner != null)
                owner.Add(current);
              else
                owner = current;
            }

            if (ch == '(') 
              owner = current;
            else if (ch == ')' && (owner.Parent != null)) 
              owner = owner.Parent;
          }
        }

        // Tail 
        if (Sb.Length > 0) {
          current = new Node(Sb.ToString());

          Sb.Length = 0;

          if (owner != null)
            owner.Add(current);
          else
            owner = current;
        }

        return owner;
      }
    }

...

  Node test = Node.Parse("f1(f2,f3,f6(f4),f5)");
  String check = test.ToString(); // <- "f1(f2,f3,f6(f4),f5)"

【讨论】:

    【解决方案2】:

    我会使用正则表达式匹配和递归调用,例如:

    class Node {
        public string val;
        public List<Node> children = new List<Node>();
    
        public static Node Parse(string input) {
            Node n = new Node();
    
            //extract value and children
            Regex reg = new Regex("(?<val>\\w+)(?:\\((?<children>(?:\\([^()]*\\)|.)*)\\))?");
    
            // fill value
            Match match = reg.Match(input);
            n.val = match.Groups["val"].Value;
    
            // if children group matched
            if (match.Groups["children"].Success) { 
                // split through commas outside of parenthesis
                string[] childrenTab = Regex.Split(match.Groups["children"].Value, ",(?![^(]*\\))");
                // and call recursively for each child
                foreach (string child in childrenTab) {
                    n.children.Add(Node.Parse(child));
                }
            }
            return n;
        }
    

    另外,静态方法似乎更合适。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-19
      相关资源
      最近更新 更多