2014-08-27 4 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 

Примечание: а, б, в и г используются для краткости. Они могут быть целыми буквами. Далее может быть и любое их число ... не просто 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 действительных «сигналы», но для базового выражения только последний является «реальным» действительным «сигнал»

мне интересно, если есть другие, более элегантные стратегии для определения того, следует ли мне конкатенировать или нет. Например, возможно, я не использую синтаксический анализатор полностью или, возможно, есть некоторые стратегии сопоставления шаблонов, с которыми я не знаком?

Итак, как мне подойти к этой проблеме?

Thx заранее

S

PS Я использую C#, но я не обязательно думать, что актуально в этом сценарии.

+0

Почему именно вам нужны «действительные сигналы»? Для меня кажется, что в первый же проход можно было исключить пробелы; кроме того, двусмысленность '-', по-видимому, не может быть устранена, оба случая должны быть рассмотрены. – Codor

+1

Будут ли элементы всегда иметь одну букву? Возможно, вы могли бы «поместить все значения в один список, удалить пробелы, вставить запятые между соседними буквами». Таким образом, '[" a "," + b "," c "]' становится '' a + bc "', который становится '" a + b, c "' – Kevin

+0

@Codor ... thx .. возможно, я haven ' Это было очень ясно. Пространство эффективно представляет элемент массива, и мне нужно знать, какие элементы используются для доступа к другой информации об этом выражении. Так что просто конкатенация в строку, а затем синтаксический анализ и просмотр дерева выражений не дают мне необходимой информации. –

ответ

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); 
} 

Каждое государство, которое мы можем иметь извлекут из этой; мы могли бы попытаться смоделировать их заранее: мы можем описать в основном два случая

  • либо мы не заботимся о следующей лексеме, потому что предыдущий маркер закончился с оператором (или мы начинаем наше первое выражение)
  • или мы хотим, чтобы следующий маркер, чтобы прибыть, чтобы начать с оператором, если мы хотим продолжить выражение

Давайте создадим первое состояние:

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 }; 
    } 
} 

Basi Как правило, мы говорим, что если токен закончил операцию, нам не нужен следующий токен, так как он будет свернут в текущее выражение: мы возвращаем 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 –

1

Кажется, что если вы удалите все пробелы, все допустимые строки будут нечетными. Затем вам нужно проверить, являются ли все нечетные позиции leters (a, b и т. Д.) И даже позиции действительными символами (+, - ,, и т. Д.).

+0

thx vm.Похоже, это становится упражнением в обучении, как лучше выразить мою проблему! Это всего лишь выборочные данные, вы можете заменить a, b, c d a строками любой длины. –

+0

Хорошо, это мне было непонятно. – Codor

+0

@Codor - я отредактирую –

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 ... Необходимо изучить его –

Смежные вопросы