![](/img/trans.png)
[英]How can I achieve non-infinite receive and send timeouts on a socket under Compact Framework?
[英]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.