Извлечение под-массивов безопасно / идиоматически в C # - PullRequest
1 голос
/ 16 августа 2011

Я создаю процессор на естественном языке в C #, и многие «слова» в нашей базе данных на самом деле представляют собой фразы из нескольких слов, которые относятся к одному существительному или действию. Пожалуйста, не обсуждайте этот проектный вызов, достаточно сказать, что он не подлежит изменению в данный момент. У меня есть строковые массивы связанных слов (кусков) предложения, которые мне нужно проверить на эти фразы и слова. Что такое идиоматический способ обработки извлечения подмассива, чтобы я меньше всего рисковал переполнением и т. П.?

Чтобы привести пример желаемой логики, позвольте мне пройти через цикл с образцом чанка. Для наших целей предположим, что единственная фраза из нескольких слов в базе данных - «быстрый коричневый».

Full phrase: The quick brown fox -> encoded as {"The", "quick", "brown", "fox"}
First iteration: Test "The quick brown fox" -> returns nothing
Second iteration: Test "The quick brown" -> returns nothing
Third iteration: Test "The quick" -> returns nothing
Fourth iteration: Test "The" -> returns value
Fifth iteration: Test "quick brown fox" -> returns nothing
Sixth iteration: Test "quick brown" -> returns value
Seventh iteration: Test "fox" -> returns value

Sum all returned values and return.

У меня есть некоторые идеи, как это сделать, но чем больше я смотрю на вещи, тем больше я действительно беспокоюсь об ошибках адресации массива и других подобных ужасах, изводящих мой код. Фраза входит в виде строкового массива, но я могу положить ее в IEnumerable. Единственное, что меня беспокоит, так это отсутствие у Enumerable индекса.

Ответы [ 4 ]

2 голосов
/ 16 августа 2011

Это звучит как идеальное приложение для алгоритма сопоставления строк Aho-Corasick.У меня есть словарь из примерно 10 миллионов фраз, через которые я провожу короткие строки.Это невероятно быстро.За один проход он расскажет вам все подходящие фразы.Таким образом, если бы в словаре присутствовали слова «the», «fox» и «quick brown», один проход вернул бы все три индекса.Найдите оригинальную статью в Интернете, и вы сможете создать ее днем.

Эффективное сопоставление строк: помощь в библиографическом поиске

1 голос
/ 16 августа 2011

Как насчет этого:

    string[] words = new string[] { "The", "quick", "brown", "fox" };

    for (int start = 0; start < words.Length - 2; start++) // at least one word
    {
        for (int end = start + 1; end < words.Length - 1; end++)
        {
            ArraySegment<string> segment = new ArraySegment<string>(words, start, end - start);
            // test segment
        }
    }

Предполагается, что вы можете использовать сегмент ArraySegment для своего теста.

1 голос
/ 16 августа 2011

Поможет ли ArraySegment или DelimitedArray?

0 голосов
/ 16 августа 2011

Путь вперед лежит в сочетании ответов Марка и Филиппа. В идеальных условиях я бы отредактировал один из их постов, но, похоже, мои правки были отклонены.

Во всяком случае, я взял DelimitedArray, с которым связался Марк, и изменил в нем несколько вещей:

Конструктор изменен на:

    public DelimitedArray(T[] array, int offset, int count, bool throwErrors = false)
    {
        this.array = array;
        this.offset = offset;
        this.count = count;
        this.throwErrors = throwErrors;
    }

Ссылка на индекс изменена на:

public T this[int index]
    {
        get
        {
            int idx = this.offset + index;
            if (idx > this.Count - 1 || idx < 0)
            {
                if (throwErrors == true)
                    throw new IndexOutOfRangeException("Index '" + idx + "' was outside the bounds of the array.");
                return default(T);
            }
            return this.array[idx];
        }
    }

Затем я применил это к использованию цикла Филиппа. Это становится:

        for (var start = 0; start < words.Length - 2; start++) // at least one word
        {
            for (var end = start + 1; end < words.Length - 1; end++)
            {
                var segment = new DelimitedArray<string>(words, start, end - start);
                lemma = string.Join(" ", segment.GetEnumerator()); // get the word/phrase to test
                result = this.DoTheTest(lemma);

                if (result > 0)
                {
                    // Add the new result
                    ret = ret + result;

                    // Move the start sentinel up, mindful of the +1 that will happen at the end of the loop
                    start = start + segment.Count - 1;
                    // And instantly finish the end sentinel; we're done here.
                    end = words.Length;
                }
            }
        }

Если бы я мог принять более одного ответа, я бы отметил оба их ответа, но поскольку оба они неполные, мне придется принять свой, когда я смогу сделать это завтра.

...