簡體   English   中英

StackOverflowException在非無限的遞歸字符串搜索中

[英]StackOverflowException in non-infinite, recursive string search

背景。 我的腳本遇到StackOverflowException,同時遞歸搜索大字符串中的特定文本。 循環不是無限的; 問題發生在9,000-10,000次合法搜索之間(對於特定搜索) - 我需要它繼續前進。 我正在使用尾遞歸(我認為),這可能是我的問題的一部分,因為我認為C#不能很好地做到這一點。 但是,我不確定如何避免在我的情況下使用尾遞歸。

問題(S)。 為什么發生StackOverflowException? 我的整體方法是否有意義? 如果設計很糟糕,我寧願從那里開始,而不僅僅是避免異常。 但如果設計可以接受,我該怎么辦StackOverflowException呢?

碼。 我編寫的課程在大量文本(大約6MB)中搜索聯系人(大約500+來自指定列表)。 我正在使用的策略是搜索姓氏,然后在姓氏之前或之后的某個地方查找名字。 我需要找到給定文本中每個聯系人的每個實例。 StringSearcher類有一個遞歸方法,它繼續搜索聯系人,每當找到一個聯系人時返回結果,但跟蹤搜索中斷的位置。

我以下列方式使用此類:

StringSearcher searcher = new StringSearcher(
    File.ReadAllText(FilePath),
    "lastname",
    "firstname",
    30
);

string searchResult = null;
while ((searchResult = searcher.NextInstance()) != null)
{
    // do something with each searchResult
}

總的來說,腳本似乎有效。 大多數聯系人返回我期望的結果。 但是,當主搜索字符串非常常見(數千次點擊),並且次要搜索字符串從未或很少發生時,似乎會出現問題。 我知道它不會卡住,因為CurrentIndex正在正常推進。

這是我正在談論的遞歸方法。

public string NextInstance()
{
    // Advance this.CurrentIndex to the next location of the primary search string
    this.SearchForNext();

    // Look a little before and after the primary search string
    this.CurrentContext = this.GetContextAtCurrentIndex();

    // Primary search string found?
    if (this.AnotherInstanceFound)
    {
        // If there is a valid secondary search string, is that found near the
        // primary search string? If not, look for the next instance of the primary
        // search string
        if (!string.IsNullOrEmpty(this.SecondarySearchString) &&
            !this.IsSecondaryFoundInContext())
        {
            return this.NextInstance();
        }
        // 
        else
        {
            return this.CurrentContext;
        }
    }
    // No more instances of the primary search string
    else
    {
        return null;
    }
}

StackOverflowException發生在this.CurrentIndex = ...中,方法如下:

private void SearchForNext()
{
    // If we've already searched once, 
    // increment the current index before searching further.
    if (0 != this.CurrentIndex)
    {
        this.CurrentIndex++;
        this.NumberOfSearches++;
    }

    this.CurrentIndex = this.Source.IndexOf(
            this.PrimarySearchString,
            ValidIndex(this.CurrentIndex),
            StringComparison.OrdinalIgnoreCase
    );

    this.AnotherInstanceFound = !(this.CurrentIndex >= 0) ? false : true;
}

如果需要,我可以包含更多代碼。 如果其中一種方法或變量值得懷疑,請告訴我。

*性能並不是真正的問題,因為這可能會在晚上作為計划任務運行。

你有一個1MB的堆棧。 當該堆棧空間用完並且您仍需要更多堆棧空間時,將拋出StackOverflowException 這可能是也可能不是無限遞歸的結果,運行時不知道。 無限遞歸只是使用更多堆棧空間的一種有效方式,然后可用(通過使用無限量)。 你可以使用一個有限的數量,這恰好比現有的更多,你會得到相同的例外。

雖然還有其他方法可以占用大量的堆棧空間,但遞歸是最有效的方法之一。 每種方法都根據該方法的簽名和本地添加更多空間。 深度遞歸可以使用大量的堆棧空間,因此如果您希望深度超過幾百個級別(甚至那么多),您可能不應該使用遞歸。 請注意,任何使用遞歸的代碼都可以迭代編寫,或使用顯式Stack

很難說,因為沒有顯示完整的實現,但基於我可以看到你或多或少編寫迭代器,但你沒有使用C#構造(即IEnumerable )。

我的猜測是“迭代器塊”將允許您使這個算法更容易編寫,更容易編寫非遞歸,並且更有效地從調用者方。

以下是如何將此方法構造為迭代器塊的高級視圖:

public static IEnumerable<string> SearchString(string text
    , string firstString, string secondString, int unknown)
{
    int lastIndexFound = text.IndexOf(firstString);

    while (lastIndexFound >= 0)
    {
        if (secondStringNearFirst(text, firstString, secondString, lastIndexFound))
        {
            yield return lastIndexFound.ToString();
        }
    }
}

private static bool secondStringNearFirst(string text
    , string firstString, string secondString, int lastIndexFound)
{
    throw new NotImplementedException();
}

這似乎不是遞歸是正確的解決方案。 通常,對於遞歸問題,您會將某些狀態傳遞給遞歸步驟。 在這種情況下,你真的有一個簡單的while循環。 下面我將你的方法體放在一個循環中,並改變遞歸步驟continue 看看是否有效......

public string NextInstance()
{
    while (true)
    {
        // Advance this.CurrentIndex to the next location of the primary search string
        this.SearchForNext();

        // Look a little before and after the primary search string
        this.CurrentContext = this.GetContextAtCurrentIndex();

        // Primary search string found?
        if (this.AnotherInstanceFound)
        {
            // If there is a valid secondary search string, is that found near the
            // primary search string? If not, look for the next instance of the primary
            // search string
            if (!string.IsNullOrEmpty(this.SecondarySearchString) &&
                !this.IsSecondaryFoundInContext())
            {
                continue; // Start searching again...
            }
            // 
            else
            {
                return this.CurrentContext;
            }
        }
        // No more instances of the primary search string
        else
        {
            return null;
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM