2014-08-27 38 views
1

我有非結構化字符串的數組(長度各不相同),我希望將它們連接成一個字符串表達式以解析。一些實例中可能是非結構化字符串連接的策略

a +b    => a+b 
a+ b    => a+b 
a +b c +d  => a+b, c+d 
a+ b c+ d  => a+b, c+d 
a+ b +c d  => a+b+c, d 
a +b +c d  => a+b+c, d 
a+ b+ c +d  => a+b+c+d 
a +b +c +d  => a+b+c+d 
a +b+ c+ d  => a+b+c+d 
a +b c d  => a+b, c, d 

NB:A,B,c和d被用於簡潔。它們實際上可以是任何長度的字符串。此外,它們可以有任意數量......不只是4.

請注意,該元素可能有前導或後綴運算符,它們將確定它是否應連接到數組中的前一項或後續項以確定是否它應該是獨立的或是下一個表達的一部分。 (還有一元運算符的固有的模糊性和確定是否

a -b => a-b or a, -b 

我有一個語法(反語),我目前使用,以確定是否被建成是公形成的表達,所以我串聯每個元素,每次一個,解析連接的結果以查看它是否格式良好,如果格式正確,我仍然需要繼續使用元素以防下一個元素具有前導運算符。一旦我得到2個無效結果解析器(或數組沒有更多的元素),我得出結論表達式(bar最後2個連接)是有效的,存儲它,然後再次啓動(我需要這樣做,因爲我需要知道有效表達式是數組中特定元素的串聯,因爲這些元素將映射回其他信息對象)

但是這一切都覺得有點遺憾。例如在

a +b +c +d 

a   => valid 
a +b  => valid 
a +b +c => valid 
a +b +c +d => valid 

的情況下,我會得到4有效的「信號」,但對基礎表達式只有最後一個是「真正的」有效「信號」

如果還有其他更多我想知道試圖確定我是否應該連接的優雅策略。 E.g也許我沒有使用解析器,或者可能存在一些我不熟悉的模式匹配策略?

那麼我該如何解決這個問題呢?

THX提前

小號

PS我使用C#,但我並不認爲這是在這種情況下有關。

+0

究竟爲什麼需要「有效信號」?對我來說,看起來好像在第一關中空間可以被消除;此外,'-'的模糊性顯然不能被消除,這兩種情況都不得不考慮。 – Codor 2014-08-27 11:55:36

+1

元素是否總是一個字母?也許你可以「把所有的值放在一個列表中,刪除空格,在相鄰的字母之間插入逗號」。所以'[ 「一」, 「+ B」, 「C」]''成爲 「A + BC」',它變成' 「A + B,C」' – Kevin 2014-08-27 12:00:13

+0

@Codor ... THX ..也許是我的天堂」非常清楚地闡明瞭它。該空間有效地表示數組元素,並且我需要知道使用哪些元素來訪問有關表達式的其他信息。所以只是連接成一個字符串,然後解析和檢查表達式樹並不能給我所需要的信息 – 2014-08-27 12:02:15

回答

1

當我有一些算法,它可以根據分立元件(如令牌,動作等),改變自己的行爲,你可以看到如果State design pattern可能是一個很好的匹配。

與其他方法相比,該模式有點冗長,但如果需要可以非常容易地擴展。我們先從一個抽象狀態類:它的目標是讓你從一個狀態改變到另一個時,一個新的令牌發揮作用:

public abstract class State 
{ 
    public static string[] operators = new string[] { "+", "-", "*", "/" }; 
    public List<string> Expressions { get; set; } 
    public List<string> Tokens { get; set; } 
    public abstract State Process(string token); 
} 

,我們可以有從這個派生每個狀態;我們可以嘗試對他們前期型號:我們基本上可以描述2案件

  • 要麼我們不關心的下一個標記,因爲前面的標識與運營商端(或我們開始我們的第一個表達式)
  • 或者我們希望下一個標記到達開始與運營商,如果我們想繼續表達

讓我們創建的第一個狀態:

public class WaitingForAnyTokenState : State 
{ 
    public override State Process(string token) 
    { 
     return PushTokenToTokenList(token); 
    } 

    protected State PushTokenToTokenList(string token) 
    { 
     Tokens.Add(token); 
     if (operators.Any(op => token.EndsWith(op))) 
     { 
      return new WaitingForAnyTokenState() { Expressions = Expressions, Tokens = Tokens }; 
     } 
     return new WaitingForOperationState() { Expressions = Expressions, Tokens = Tokens }; 
    } 
} 

巴斯cally,我們說如果令牌以操作結束,我們不關心下一個令牌,因爲它會被摺疊到當前表達式中:我們返回WaitingForAnyTokenState

相反,如果我們不與操作結束了,會發生什麼情況,以目前的表達依賴於下一個標記。如果它從一個操作開始,表達式會繼續。如果不是,當前表達式結束,我們開始一個新的表達式。

public class WaitingForOperationState : State 
{ 
    public override State Process(string token) 
    { 
     CloseCurrentExpression(token); 
     return PushTokenToTokenList(token); // let's imagine the same method as above is accessible here 
    } 

    private void CloseCurrentExpression(string token) 
    { 
     if (!operators.Any(op => token.StartsWith(op))) 
     { 
      CombineTokensIntoExpression(); 
      Tokens = new List<string>(); 
     } 
    } 
} 

有趣的是,下一個案件仍然以與第一個國家相同的方式決定。唯一需要改變的是當前表達式的關閉。

這裏是建築的示例代碼,你可以使用:

private static void Main(string[] args) 
{ 
    var ttea = new TokenToExpressionAggregator(); 
    foreach (var l in new string[] { "a+", "+1", "+c-", "d", "e", "+d", "z+", "a+" }) { 
     ttea.Add(l); 
    } 
    ttea.EndAggregation(); 
    foreach (var expression in ttea.CurrentState.Expressions) { 
     Console.WriteLine(expression); 
    } 
} 

public class TokenToExpressionAggregator 
{ 
    public State CurrentState { get; set; } 
    public TokenToExpressionAggregator() 
    { 
     CurrentState = new InitialState(); 
    } 
    public void Add(string token) 
    { 
     CurrentState = CurrentState.Process(token); 
    } 
    public void EndAggregation() 
    { 
     CurrentState = new FinalState(CurrentState); 
    } 
} 

public abstract class State 
{ 
    public static string[] operators = new string[] { "+", "-", "*", "/" }; 
    public List<string> Expressions { get; set; } 
    public List<string> Tokens { get; set; } 
    public abstract State Process(string token); 

    protected State PushTokenToTokenList(string token) 
    { 
     Tokens.Add(token); 
     if (operators.Any(op => token.EndsWith(op))) 
     { 
      return new WaitingForAnyTokenState() { Expressions = Expressions, Tokens = Tokens }; 
     } 
     return new WaitingForOperationState() { Expressions = Expressions, Tokens = Tokens }; 
    } 

    protected void CombineTokensIntoExpression() 
    { 
     Expressions.Add(string.Join(" ", Tokens.ToArray())); 
    } 
} 

public class InitialState : WaitingForAnyTokenState 
{ 
    public InitialState() 
    { 
     Expressions = new List<string>(); 
     Tokens = new List<string>(); 
    } 
} 

public class WaitingForAnyTokenState : State 
{ 
    public override State Process(string token) 
    { 
     return PushTokenToTokenList(token); 
    } 
} 

public class WaitingForOperationState : State 
{ 
    public override State Process(string token) 
    { 
     CloseCurrentExpression(token); 
     return PushTokenToTokenList(token); 
    } 

    private void CloseCurrentExpression(string token) 
    { 
     if (!operators.Any(op => token.StartsWith(op))) 
     { 
      CombineTokensIntoExpression(); 
      Tokens = new List<string>(); 
     } 
    } 
} 

public class FinalState : State 
{ 
    public FinalState(State state) 
    { 
     Expressions = state.Expressions; 
     Tokens = state.Tokens; 
     CombineTokensIntoExpression(); 
     Tokens = null; 
    } 

    public override State Process(string token) 
    { 
     return this; 
    } 
} 

它使冗長,但雄辯的代碼;個人品味和職業環境可能會抗議。但我發現它有助於表達你的狀態之間的功能轉換。由於每個州都很小,並且不依賴於以前的狀態,因此它也更容易測試。我已經採取了一些自由猜測在你的過程中的一些不清楚的地方(是連接運營商無效?),它不是完整的(沒有括號),但我認爲這種結構可以幫助你的那種標記流你似乎有。

+0

TVM。優秀。我沒有想到在這種情況下的狀態模式,但它有意義,它似乎與解析密切相關。將它打散得很好IMV – 2014-08-28 05:12:14

1

看來,如果你刪除所有空格,所有有效的字符串都是奇數。接下來,您需要檢查所有奇數位置是否是槓桿(a,b等),甚至是有效字符(+, - 等)。

+0

thx vm。看起來這正在成爲學習如何更好地表達我的問題的練習!這只是示例數據,你可以用任意長度的字符串替換a,b,c d a – 2014-08-27 12:06:46

+0

好的,這對我來說並不清楚。 – Codor 2014-08-27 12:07:06

+0

@Codor - 我將編輯 – 2014-08-27 12:07:56

2

這應該工作,注意這個代碼是如何處理的單目運算符

static List<string> GetExpressions(string[] stringArray) 
    { 
     const string operators = "+-*/="; 
     const string unaryOps = "+-"; 
     var q = new Queue<string>(stringArray.Length*2); 

     foreach (string s in stringArray) 
     { 
      var work = s; 
      if (operators.Contains(work[0])) 
      { 
       q.Enqueue(work[0].ToString()); 
       work = work.Substring(1); 
      } 
      if (operators.Contains(work[work.Length-1])) 
      { 
       q.Enqueue(work.Substring(0, work.Length - 1)); 
       q.Enqueue(work[work.Length - 1].ToString()); 
       continue; 
      } 
      q.Enqueue(work); 
     } 

     var res = new List<string>(); 
     var tmpString = new StringBuilder(); 
     var lastState = "Op"; 

     while (q.Count > 0) 
     { 
      var currElem = q.Dequeue(); 
      var currState = "St"; 
      if (unaryOps.Contains(currElem)) 
       currState = "Un"; 
      else if (operators.Contains(currElem)) 
       currState = "Op"; 

      switch (lastState + currState) 
      { 
       case "OpUn": 
       case "OpSt": 
       case "UnUn": // only with + & - unary ops: refinement necessary 
       case "UnSt": 
       case "StUn": // only with + & - unary ops: refinement necessary 
       case "StOp": 
        tmpString.Append(currElem); 
        break; 
       case "StSt": 
        res.Add(tmpString.ToString()); 
        tmpString.Length=0; 
        tmpString.Append(currElem); 
        break; 
       case "OpOp": 
       case "UnOp": 
        throw new Exception(); 
      } 
      lastState = currState; 
     } 

     res.Add(tmpString.ToString()); 

     return res; 
    } 
+0

Thx vm ...需要研究它 – 2014-08-27 13:41:01