2011-07-27 54 views
5

我一直在實現一個增強型Shunting-Yard算法來解析算術表達式。該算法的一個方面是維護QueueStack類型安全的方法在集合中存儲兩種類型的對象

在我的實施中,Queue包含ExpressionsOperatorsStack包含OperatorsParenthesis

Expressions,ParenthesisOperators沒有任何共同之處,它們保證它們中的任何兩個具有共享接口。

途徑:

  • 我目前實現由ExpressionOperator實施INotParanthesisOperatorParanthesis執行INotExpression。然後我宣佈Queue <INotParanthesis>Stack <INotExpression>

    我不喜歡這個實現 - 這些接口對於更乾淨的算法代碼來說似乎非常黑客。我也相信接口應該描述一個對象是什麼,而不是它不是。

  • 另一方面,我也不想使用<Object>的集合,因爲可能很難確定這些代碼的正確性。

  • 到目前爲止,我唯一想到的就是實現我自己的NonParanthesisQueueNonExpressionStack容器。這具有對從這些容器中拉出的對象進行更一致的類型檢查的優點 - 以及更多代碼的缺點。

是否有任何合理的替代方案我的方法?

回答

4

這聽起來像你真正想要的是和類型。雖然C#沒有內置這些​​內容,但函數式編程還有一個技巧可以使用,稱爲Church編碼來實現這一點。它完全是類型安全的,不涉及任何類型的轉換,但是由於類型推斷的侷限性,在C#中使用它有點奇怪。

主要技巧是,不是使用屬性和檢查來檢索兩個選項之一,我們有一個高階函數Map,它接受兩個函數作爲參數,並根據所存在的替代方法調用相應的函數。這裏是你將如何使用它:

var stack = new Stack<IEither<Operator, Parenthesis>>(); 

stack.Push(new Left<Operator, Parenthesis>(new Operator())); 
stack.Push(new Right<Operator, Parenthesis>(new Parenthesis())); 

while (stack.Count > 0) 
{ 
    stack.Pop().Map(op => Console.WriteLine("Found an operator: " + op), 
        par => Console.WriteLine("Found a parenthesis: " + par)); 
} 

這裏是IEitherLeftRight實施。它們是完全通用的,可用於任何你想要求和類型的地方。

public interface IEither<TLeft, TRight> 
{ 
    TResult Map<TResult>(Func<TLeft, TResult> onLeft, Func<TRight, TResult> onRight); 
    void Map(Action<TLeft> onLeft, Action<TRight> onRight); 
} 

public sealed class Left<TLeft, TRight> : IEither<TLeft, TRight> 
{ 
    private readonly TLeft value; 

    public Left(TLeft value) 
    { 
     this.value = value; 
    } 

    public TResult Map<TResult>(Func<TLeft, TResult> onLeft, Func<TRight, TResult> onRight) 
    { 
     return onLeft(value); 
    } 

    public void Map(Action<TLeft> onLeft, Action<TRight> onRight) 
    { 
     onLeft(value); 
    } 
} 

public sealed class Right<TLeft, TRight> : IEither<TLeft, TRight> 
{ 
    private readonly TRight value; 

    public Right(TRight value) 
    { 
     this.value = value; 
    } 

    public TResult Map<TResult>(Func<TLeft, TResult> onLeft, Func<TRight, TResult> onRight) 
    { 
     return onRight(value); 
    } 

    public void Map(Action<TLeft> onLeft, Action<TRight> onRight) 
    { 
     onRight(value); 
    } 
} 

參考文獻:

+0

謝謝!這正是我所期待的。 – Vladislav

+0

@Vladislav這與類似於理查爾的答案(基本上這個想法是使用元組類型來保存異類數據),但我想你可以結合兩者的想法使它更短(我說這是因爲你喜歡它不詳細)。 – nawfal

1

也許你可以爲每一個定義一個小的持有者類型,一個帶有Expression屬性和一個操作符屬性,另一個帶有一個操作符屬性和一個括號屬性。訪問者和構造函數可以聲明或以其他方式確保只有一個被填充。隊列和堆棧將分別包含適當的持有者類型。

有點尷尬,但typeafe和可行。

希望有人會有一個更聰明的想法。

+0

謝謝 - 那是不是我考慮的 - 肯定是有道理的,儘管是利特詳細。 – Vladislav

0

如果名字困擾你,IQueueable和IStackable如何?以下是一些與你的相似的其他stackoverflow問題(關於標記接口)。

What is the purpose of a marker interface?

Interface without any members - bad practice?

+0

我的擔心是我爲這些類提供了接口,僅用於實現此算法。關於表情本身並沒有什麼不可拆卸的地方,關於括號也沒有內在的不可分割的地方。如果我改變我的分析算法,這些接口將只是在課堂上的重量。 – Vladislav

相關問題