C# 列表<;T>;还有数不清的差别
在实现这个泛型的过程中,我无意中发现了IEnumerable和List之间的一个差异,我需要帮助找出这个差异 这是我的分类C# 列表<;T>;还有数不清的差别,c#,generics,ienumerable,C#,Generics,Ienumerable,在实现这个泛型的过程中,我无意中发现了IEnumerable和List之间的一个差异,我需要帮助找出这个差异 这是我的分类 public class MergeSort<T> { public IEnumerable<T> Sort(IEnumerable<T> arr) { if (arr.Count() <= 1) return arr; int middle = arr.Count() / 2;
public class MergeSort<T>
{
public IEnumerable<T> Sort(IEnumerable<T> arr)
{
if (arr.Count() <= 1) return arr;
int middle = arr.Count() / 2;
var left = arr.Take(middle).ToList();
var right = arr.Skip(middle).ToList();
return Merge(Sort(left), Sort(right));
}
private static IEnumerable<T> Merge(IEnumerable<T> left, IEnumerable<T> right)
{
var arrSorted = new List<T>();
while (left.Count() > 0 && right.Count() > 0)
{
if (Comparer<T>.Default.Compare(left.First(), right.First()) < 0)
{
arrSorted.Add(left.First());
left=left.Skip(1);
}
else
{
arrSorted.Add(right.First());
right=right.Skip(1);
}
}
return arrSorted.Concat(left).Concat(right);
}
}
使用.ToList()
编辑
这是我愚蠢的测试,让我
我是这样测试的:
var sortedInts = mergeSortInt.Sort(ints);
ints.Sort();
if (Enumerable.SequenceEqual(ints, sortedInts)) Console.WriteLine("ints sorts ok");
只是把第一行改成
var sortedInts = mergeSortInt.Sort(ints).ToList();
删除问题(以及延迟计算)
编辑2010-12-29
我想我会明白懒惰的评估是如何把事情搞砸的,但我就是不明白 在上面的排序方法中删除
.ToList()
,如下所示
var left = arr.Take(middle);
var right = arr.Skip(middle);
那就试试这个
var ints = new List<int> { 5, 8, 2 };
var mergeSortInt = new MergeSort<int>();
var sortedInts = mergeSortInt.Sort(ints);
ints.Sort();
if (Enumerable.SequenceEqual(ints, sortedInts)) Console.WriteLine("ints sorts ok");
但是在ints.Sort()之后它返回
[0]: 2
[1]: 5
[2]: 5
这里到底发生了什么?问题是你要按左右排序,而不是按右边排序,然后合并成一个序列。这并不意味着你得到了一个完全排序的序列
首先必须合并,然后必须排序:
public IEnumerable<T> Sort(IEnumerable<T> arr)
{
if (arr.Count() <= 1) return arr;
int middle = arr.Count() / 2;
var left = arr.Take(middle).ToList();
var right = arr.Skip(middle).ToList();
// first merge and than sort
return Sort(Merge(left, right));
}
公共IEnumerable排序(IEnumerable arr)
{
如果(arr.Count()我在有列表和没有列表的情况下运行了它,它就工作了。
无论如何,合并排序的优点之一是它能够使用O(1)进行就地排序空间复杂性,这一实现将不会受益。无法复制-我刚刚尝试了这个,它工作得非常好。显然,它在各种方面都相当低效,但是删除ToList
调用并没有使它失败
这是我的测试代码,您的MergeSort
代码保持原样,但没有ToList()
调用:
using System;
using System.Collections.Generic;
public static class Extensions
{
public static void Dump<T>(this IEnumerable<T> items, string name)
{
Console.WriteLine(name);
foreach (T item in items)
{
Console.Write(item);
Console.Write(" ");
}
Console.WriteLine();
}
}
class Test
{
static void Main()
{
var ints = new List<int> { 5, 8, 2, 1, 7 };
var mergeSortInt = new MergeSort<int>();
var sortedInts = mergeSortInt.Sort(ints);
sortedInts.Dump("Sorted");
}
}
可能问题在于您是如何测试代码的?您的函数是正确的-如果您检查合并的结果,您将看到结果已排序。
那么问题出在哪里呢?正如您所怀疑的,您的测试是错误的-当您在原始列表上调用Sort
时,您会更改从它派生的所有集合!
下面是一个演示您所做工作的片段:
List<int> numbers = new List<int> {5, 4};
IEnumerable<int> first = numbers.Take(1);
Console.WriteLine(first.Single()); //prints 5
numbers.Sort();
Console.WriteLine(first.Single()); //prints 4!
在Ideone上也有同样的例子:这完全违背了合并排序的目的-你也可以排序(arr)
-你建议拆分和合并(=什么都不做),然后排序:)
合并排序并不声称是O(1)复杂度。它是O(n log n)。它没有声明,但可以在O(1)中实现空间复杂度和O(nlgn)时间复杂度的一个小常数因子都是这里所缺少的。啊,你是说空间复杂度吗?我从没听说过“地点复杂度”以前。如果是这样,是的,那是真的。这在很多方面都是低效的,但我认为这是用于教育实验,这很好。必须说,在删除ToList()
后,它在我的电脑上运行得很好。下面是另一个无法重现的示例(使用mono):我看到的唯一可疑部分是left=left.Skip(1)
,这可能会有执行延迟的问题,但我不知道如何解决。我在问题中添加了一个新的示例,因为我仍然无法准确地了解这里发生了什么。我已经实现了一个完全懒惰的评估版本,我想我会弄清楚懒惰的评估是如何把事情搞砸的,但我不明白。我添加了一个新的示例这个问题的例子。我明白了。我还明白可变集合和惰性求值是一个糟糕的组合。我不明白的是,在这种情况下,它是如何扰乱排序的,因为已经排序的列表不应该是一个问题。除非我的小MergeSort不是完全惰性的,而是半惰性的。@Jonas-我已经用mor更新了我的解释e详细信息,您的代码中似乎不止一点复杂。
:)
精彩的解释!现在我终于明白了。谢谢!我实现了一个完全懒惰的评估版本,并在博客中介绍了它和我面临的问题。@Jonas-谢谢大家的关注,很高兴能提供帮助!
[0]: 2
[1]: 5
[2]: 5
public IEnumerable<T> Sort(IEnumerable<T> arr)
{
if (arr.Count() <= 1) return arr;
int middle = arr.Count() / 2;
var left = arr.Take(middle).ToList();
var right = arr.Skip(middle).ToList();
// first merge and than sort
return Sort(Merge(left, right));
}
using System;
using System.Collections.Generic;
public static class Extensions
{
public static void Dump<T>(this IEnumerable<T> items, string name)
{
Console.WriteLine(name);
foreach (T item in items)
{
Console.Write(item);
Console.Write(" ");
}
Console.WriteLine();
}
}
class Test
{
static void Main()
{
var ints = new List<int> { 5, 8, 2, 1, 7 };
var mergeSortInt = new MergeSort<int>();
var sortedInts = mergeSortInt.Sort(ints);
sortedInts.Dump("Sorted");
}
}
Sorted
1 2 5 7 8
List<int> numbers = new List<int> {5, 4};
IEnumerable<int> first = numbers.Take(1);
Console.WriteLine(first.Single()); //prints 5
numbers.Sort();
Console.WriteLine(first.Single()); //prints 4!
var ints = new List<int> { 3,2,1 };
var mergeSortInt = new MergeSort<int>();
var sortedInts = mergeSortInt.Sort(ints);
// sortedInts is { 1, 2, 3 }
for(int i=0;i<ints.Count;i++) ints[i] = -i * 10;
// sortedInts is { 1, 2, 0 }