【问题标题】:Extracting sub-arrays safely/idiomatically in C#在 C# 中安全/惯用地提取子数组
【发布时间】:2011-10-27 14:02:42
【问题描述】:

我正在用 C# 构建一个自然语言处理器,我们数据库中的许多“词”实际上是指一个名词或动作的多词短语。请不要讨论这个设计调用,只要说它目前不可更改就足够了。我有我需要测试这些短语和单词的句子相关单词(块)的字符串数组。 什么是处理子数组提取的适当惯用方法,这样我就可以将溢出错误等风险降到最低?

为了给出所需逻辑的示例,让我通过一个示例块逐步运行。出于我们的目的,假设数据库中唯一的多词短语是“quick brown”。

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 缺少索引。

【问题讨论】:

  • 为什么两个词的短语最初没有编码为 {"The", "quick brown", "fox"} ??
  • 您是否在询问如何为每次迭代生成测试短语?
  • @sllev:因为我们不知道它是一个多词短语,直到它进入数据库并返回。在此之前,它只是根据相关的语法角色(主语从句、动词从句等)进行编码。它从那里分解成该子句中单词的字符串数组。
  • @Philip:有点像?我知道如何从子数组中生成字符串。问题是确保我得到子数组的方式不会在处理整数寻址或其他东西时搞砸。

标签: c# arrays string sub-array


【解决方案1】:

这听起来像是 Aho-Corasick 字符串匹配算法的完美应用。我有一本包含大约 1000 万个短语的字典,我可以通过这些短语来处理短字符串。它非常快。只需通过一次,它就会告诉您所有匹配的短语。因此,如果“the”、“fox”和“quick brown”都在字典中,则单次传递将返回所有三个索引。

这很容易实现。在网上找到原论文,一个下午就可以完成。

Efficient String Matching: An Aid to Bibliographic Search

【讨论】:

  • 这很棒(我现在正在查找它的实现),但是我如何准确地从传入的数组中获取这三个字符串,特别是如果两者都“快速”和“棕色”也在字典里?
  • 阅读原始论文,这将比我能解释得更好。如果您遇到我发布的实现,请忽略它。我在发布该算法时误解了该算法,并且没有机会更正它。请参阅我编辑的回复以获取原始论文的链接。
  • 这是该算法在 C# 中的实现:informit.com/guides/content.aspx?g=dotnet&seqNum=869
【解决方案2】:

ArraySegmentDelimitedArray 有帮助吗?

【讨论】:

  • 现在正在试验这个。我最初的反应不是最好的,因为构造函数是(baseArray,offset,count)。如果 count 变量由于某种原因关闭,我仍然会破坏数组的顶部并获得异常,不是吗?如果我可以分配开始和结束偏移量,我会感觉更舒服,但我会继续玩这个,看看我是否能比我之前编写的丑陋代码更好地处理它。
  • count = endIndex - startIndex
【解决方案3】:

这样的事情怎么样:

    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 段进行测试。

【讨论】:

  • 我最初做了这样的事情(我没有使用 ArraySegment,但它是一个嵌入式循环),基本上它工作得很好,直到你遇到一个多词短语,此时你必须用 start 参数做一些令人作呕的黑客攻击,以确保它保持在目标上。它使代码非常难以阅读,并严重损害了我的信心,即它可以在我汇总的单元测试之外的大多数情况下工作。
  • 你能举一个“多词短语”的例子吗?你遇到了什么问题?
  • 我指的是“棕狐”之类的东西,但现在没关系,在您的代码示例和 Mark 给出的 DelimitedArray 之间,我有一个我很熟悉的过程。我对 DelimitedArray 进行了一些更改,以使其行为符合我的要求。我将对您的帖子进行编辑以展示我所做的事情;请注意。
【解决方案4】:

这里的前进道路在于结合马克和菲利普的答案。在理想情况下,我会用它编辑他们的一篇帖子,但看起来我的编辑被拒绝了。

不管怎样,我拿了 Mark 链接的 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];
        }
    }

然后我将其用于 Philipp 的循环使用。这变成:

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

如果我可以接受多个答案,我会标记他们的两个答案,但由于它们都不完整,我将不得不接受我自己的答案,当我明天能够这样做时。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-21
    • 1970-01-01
    • 1970-01-01
    • 2012-04-21
    • 2016-05-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多