Стратегия конкатенации неструктурированных строк

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 действительных "сигнала", но для основного выражения только последний был "реальным" действительным "сигналом",

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

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

спасибо заранее

S

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

  • 0
    Почему именно вам нужны «действительные сигналы»? Мне кажется, что в первом проходе только пробелы могут быть устранены; Кроме того, двусмысленность - очевидно, не может быть удалена, оба случая должны быть рассмотрены.
  • 1
    Будут ли элементы всегда состоять из одной буквы? Возможно, вы могли бы сделать «поместить все значения в один список, убрать пробелы, вставить запятые между соседними буквами». Таким образом, ["a","+b","c"] становится "a+bc" , что становится "a+b, c"
Показать ещё 7 комментариев
Теги:
algorithm

3 ответа

1
Лучший ответ

Когда у меня есть алгоритм, который может изменить его поведение в зависимости от дискретных элементов (например, токенов, действий и т.д.), Вы можете увидеть, может ли шаблон дизайна штата быть хорошим.

Шаблон немного подробный по сравнению с другими методами, но при необходимости он легко расширяется. Мы начинаем с абстрактного класса состояний: его цель - позволить вам перейти от одного состояния к другому, когда вступает в игру новый токен:

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

В принципе, мы говорим, что если токен закончил операцию, нам не WaitingForAnyTokenState следующий токен, поскольку он будет сложен в текущее выражение: мы возвращаем WaitingForAnyTokenState.

Наоборот, если мы не закончим операцию, то то, что происходит с текущим выражением, зависит от следующего токена. Если он начинается с операции, выражение продолжается. Если нет, текущее выражение заканчивается, и мы начинаем новый.

public class WaitingForOperationState : State
{
    public override State Process(string token)
    {
        CloseCurrentExpression(token);
        return PushTokenToTokenList(token); // let 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
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 ... нужно изучить это
1

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

  • 0
    спасибо Похоже, это становится упражнением в изучении того, как лучше выразить мою проблему !! Это просто пример данных, вы можете заменить a, b, cda на строки любой длины
  • 0
    Хорошо, это не было ясно для меня.
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню