【问题标题】:C# functional quicksort is failingC# 功能快速排序失败
【发布时间】:2011-02-11 21:13:01
【问题描述】:

我正在尝试使用 linq 使用 C# 以功能样式实现快速排序,而这段代码随机工作/不工作,我不知道为什么。
重要的是要提到:当我在数组或列表上调用它时,它工作正常。但是在未知的 IEnumerable 上,它会变得疯狂(通常会丢失值或崩溃。有时会起作用。)
代码:

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

【问题讨论】:

  • unknown-what-it-really-is IEnumerable 是什么意思??这是一个通用方法,因此您的对象的类型始终是已知的。
  • 我的意思是我不知道 IEnumerable shell 下是什么。是清单吗?数组?我尝试的和失败的来自一个列表,我基本上做了“Random rand = ...; int[100].Select(a=>rand.Next());”

标签: c# linq functional-programming quicksort


【解决方案1】:

一些可枚举的实例,例如由 Linq to SQL 或 Entity Framework 查询返回的实例,仅设计为迭代一次。您的代码需要多次迭代,并且会在这些类型的实例上崩溃或表现异常。您必须首先使用 ToArray() 或类似方法实现这些可枚举。

您还应该重用 pivot,这样您就不必继续迭代第一个和剩余的元素。这可能无法完全解决问题,但在某些情况下会有所帮助:

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

(您也不需要迭代通过sortedQuery - 只需返回它,它已经是IEnumerable&lt;T&gt;。)

在相关说明中,您为什么觉得需要重新实现此功能? Enumerable.OrderBy 已经为你做了。


对更新的回应:

您的测试失败是因为您的测试错误,而不是算法。

Random 是一个非确定性输入源,正如我在上面解释的,排序方法需要在同一个序列上执行多次迭代。如果序列是完全随机的,那么它将在后续迭代中得到不同的值。本质上,您是在尝试对元素不断变化的序列进行快速排序!

如果您希望测试成功,您需要使输入一致。使用 seed 作为随机数生成器:

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

它将返回排序。

【讨论】:

  • 我的可枚举实际上只是 Enumerable.Range,但它仍然失败。此外,我尝试只返回 sortedQuery,但出现错误 - “无法从迭代器返回值。使用 yield return 语句返回值,或使用 yield break 结束迭代。”而且 - 而且 - 我不需要实现这个,我只是想尝试学习函数式编程。
  • @Rubys:您对“无法返回值”错误是正确的 - 我刚刚解决了这个问题,问题是一开始的 yield break 与直接返回混合在一起在末尾。我会用Enumerable.Range 试试这个,看看会发生什么。
  • @Rubys:在这里的Enumerable.Range 上工作得很好。发布失败的测试代码。
  • 更新了主帖以包含测试代码。 Enuerable.Range 似乎有效,它确实失败了一次,可能是我的错误,但我现在写的测试肯定失败多于胜利。
  • 嗯,这是有道理的。具有讽刺意味的是,因为它以一种非常难以理解的功能方式失败,而我试图完成相反的事情。谢谢你:)
猜你喜欢
  • 1970-01-01
  • 2017-02-05
  • 2017-10-07
  • 2014-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-05
  • 2018-05-17
相关资源
最近更新 更多