Dot-Net
翻譯表達式樹時如何推斷括號的使用?
我正在將表達式樹轉換為類似於中綴符號的格式;我不是在評估樹或執行它的操作。樹包含邏輯和關係操作,我想在翻譯過程中以智能的方式發出括號。
為了說明,請考慮以下人為的表達式:
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。Precedesprotected 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 * } } }