Dot-Net

翻譯表達式樹時如何推斷括號的使用?

  • August 24, 2012

我正在將表達式樹轉換為類似於中綴符號的格式;我不是在評估樹或執行它的操作。樹包含邏輯和關係操作,我想在翻譯過程中以智能的方式發出括號。

為了說明,請考慮以下人為的表達式:

a < x & (a < y | a == c) & a != d

如果我按順序遍歷這個表達式產生的表達式樹,那麼我會列印出下面的表達式,這是不正確的。

a < x & a < y | a == c & a != d
// equivalent to (a < x & a < y) | (a == c & a != d)

或者,我可以再次執行中序遍歷,但在處理二進製表達式之前和之後發出括號。這將產生以下正確的表達式,但有幾個多餘的括號。

(((a < x) & ((a < y) | (a == c))) & (a != d))

是否有一個表達式樹遍曆算法可以產生最佳括號表達式?

作為參考,這是ExpressionVisitor我用來檢查樹的片段。

class MyVisitor : ExpressionVisitor
{
   protected override Expression VisitBinary(BinaryExpression node)
   {
       Console.Write("(");

       Visit(node.Left);
       Console.WriteLine(node.NodeType.ToString());
       Visit(node.Right);

       Console.Write(")");

       return node;
   }

   // VisitConstant, VisitMember, and VisitParameter omitted for brevity.
}

嘗試這樣的事情,假設它node.NodeType是 type ,並且如果第一個參數在第二個參數之前NodeType,該函式存在並返回 true。Precedes

protected override Expression Visit(BinaryExpression node, NodeType parentType)
{
   bool useParenthesis = Precedes(parentType, node.NodeType);

   if (useParenthesis)
       Console.Write("(");

   Visit(node.Left, node.NodeType);
   Console.WriteLine(node.NodeType.ToString());
   Visit(node.Right, node.NodeType);

   if (useParenthesis)
       Console.Write(")");

   return node;
}

我接受了Dialecticus 的答案,因為它為實現該算法提供了良好的基礎。這個答案的唯一問題是它要求VisitBinary()方法知道它的父呼叫者作為方法參數,這是不可行的,因為這些方法是基方法的重載。

我提供了以下解決方案,它使用類似的算法,但應用檢查以在表達式樹的子節點的父呼叫中發出括號。

class MyVisitor : ExpressionVisitor
{
   private readonly IComparer<ExpressionType> m_comparer = new OperatorPrecedenceComparer();

   protected override Expression VisitBinary(BinaryExpression node)
   {
       Visit(node, node.Left);
       Console.Write(node.NodeType.ToString());
       Visit(node, node.Right);

       return node;
   }

   private void Visit(Expression parent, Expression child)
   {
       if (m_comparer.Compare(child.NodeType, parent.NodeType) < 0)
       {
           Console.Write("(");
           base.Visit(child);
           Console.Write(")");
       }
       else
       {
           base.Visit(child);
       }
   }

   // VisitConstant, VisitMember, and VisitParameter omitted for brevity.
}

優先級比較函式實現為IComparer<ExpressionType>,它應用 C#運算符優先級規則。

class OperatorPrecedenceComparer : Comparer<ExpressionType>
{
   public override int Compare(ExpressionType x, ExpressionType y)
   {
       return Precedence(x).CompareTo(Precedence(y));
   }

   private int Precedence(ExpressionType expressionType)
   {
       switch(expressionType) { /* group expressions and return precedence ordinal * }
   }
}

引用自:https://stackoverflow.com/questions/12057519