2010-04-24 138 views
9

我想實現使用C#使用LINQ的功能風格的快速排序,並且此代碼隨機工作/不工作,我不明白爲什麼。
重要提醒:當我在數組或列表上調用它時,它工作正常。可是,在一個未知的 - 什麼 - 它,真的,是IEnumerable的,它會瘋狂(失去價值或崩潰,通常,有時作品。)
代碼:C#功能快速排序失敗

public static IEnumerable<T> Quicksort<T>(this IEnumerable<T> source) 
     where T : IComparable<T> 
    { 
     if (!source.Any()) 
      yield break; 
     var pivot = source.First(); 
     var sortedQuery = source.Skip(1).Where(a => a.CompareTo(source.First()) <= 0).Quicksort() 
          .Concat(new[] { pivot }) 
          .Concat(source.Skip(1).Where(a => a.CompareTo(source.First()) > 0).Quicksort()); 
     foreach (T key in sortedQuery) 
      yield return key; 
    } 

你能找到的任何故障這會導致這個失敗?

編輯:一些更好的測試代碼:

 var rand = new Random(); 
     var ienum = Enumerable.Range(1, 100).Select(a => rand.Next()); 
     var array = ienum.ToArray(); 
     try 
     { 
      array.Quicksort().Count(); 
      Console.WriteLine("Array went fine."); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("Array did not go fine ({0}).", ex.Message); 
     } 
     try 
     { 
      ienum.Quicksort().Count(); 
      Console.WriteLine("IEnumerable went fine."); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("IEnumerable did not go fine ({0}).", ex.Message); 
     } 
+1

你是什麼意思'未知 - 它真的是IEnumerable'?這是一個通用的方法,所以你的對象的類型總是已知的。 – 2010-04-24 14:32:17

+0

我的意思是我不知道IEnumerable shell下面是什麼。這是一個列表嗎?數組?我嘗試過的和失敗的是從一個列表中,我基本上做了「Random rand = ...; int [100] .Select(a => rand.Next());」 – Rubys 2010-04-24 14:41:03

回答

7

一些枚舉情況下,如由返回的LINQ to SQL或實體框架查詢,僅設計進行一次迭代。您的代碼需要多次迭代,並且會在這些類型的實例上崩潰或行爲異常。你必須首先用ToArray()或類似的方法實現這些枚舉。

您還應該重複使用該pivot,以便您不必爲第一個元素和其餘元素進行迭代。這可能不會完全解決問題,但它會在某些情況下幫助:(您還沒有通過sortedQuery需要迭代 - 只返回它,它已經是一個IEnumerable<T>

public static IEnumerable<T> Quicksort<T>(this IEnumerable<T> source) 
    where T : IComparable<T> 
{ 
    if (!source.Any()) 
     return source; 
    var pivot = source.First(); 
    var remaining = source.Skip(1); 
    return remaining 
     .Where(a => a.CompareTo(pivot) <= 0).Quicksort() 
     .Concat(new[] { pivot }) 
     .Concat(remaining.Where(a => a.CompareTo(pivot) > 0).Quicksort()); 
} 

在相關說明中,爲什麼您覺得需要重新實現此功能? Enumerable.OrderBy已經爲你做。


響應更新:

你的測試失敗,因爲測試是錯誤,而不是算法。

Random是一個非確定性的輸入源,正如我上面所解釋的,排序方法需要在同一個序列上執行多次迭代。如果序列是完全隨機的,那麼在隨後的迭代中將得到不同的值。本質上,你正試圖快速排序一個元素不斷變化的序列!

如果您希望測試成功,您需要使輸入一致。使用種子隨機數發生器:

static IEnumerable<int> GetRandomInput(int seed, int length) 
{ 
    Random rand = new Random(seed); 
    for (int i = 0; i < length; i++) 
    { 
     yield return rand.Next(); 
    } 
} 

然後:

static void Main(string[] args) 
{ 
    var sequence = GetRandomInput(248917, 100); 
    int lastNum = 0; 
    bool isSorted = true; 
    foreach (int num in sequence.Quicksort()) 
    { 
     if (num < lastNum) 
     { 
      isSorted = false; 
      break; 
     } 
     lastNum = num; 
    } 
    Console.WriteLine(isSorted ? "Sorted" : "Not sorted"); 
    Console.ReadLine(); 
} 

它會回來的排序。

+0

我的enumerable實際上只是Enumerable.Range,它仍然失敗。 此外,我嘗試只返回sortedQuery,但我得到一個錯誤 - 「不能從一個迭代器中返回一個值。使用yield return語句返回一個值,或者產生break來結束迭代。」 而且 - 而且 - 我不需要實現這一點,我只是想嘗試學習函數式編程。 – Rubys 2010-04-24 14:44:34

+0

@Rubys:你說的「無法返回值」的錯誤是正確的 - 我剛剛解決了這個問題,那個問題就是開始時的「收益率突破」,並且最終與直接回報混合在一起。我會用'Enumerable.Range'來試試這個,看看會發生什麼。 – Aaronaught 2010-04-24 14:53:07

+0

@Rubys:在這裏'Enumerable.Range'絕對正常。發佈失敗的測試代碼。 – Aaronaught 2010-04-24 14:55:42