我随机排序了IEnumerable
。我一直在打印相同的元素,并得到不同的结果。
string[] collection = {"Zero", "One", "Two", "Three", "Four"};
var random = new Random();
var enumerableCollection = collection.OrderBy(e => random.NextDouble());
Console.WriteLine(enumerableCollection.ElementAt(0));
Console.WriteLine(enumerableCollection.ElementAt(0));
Console.WriteLine(enumerableCollection.ElementAt(0));
Console.WriteLine(enumerableCollection.ElementAt(0));
Console.WriteLine(enumerableCollection.ElementAt(0));
每次写入都给出不同的随机元素。为什么不保留订单?
答案 0 :(得分:5)
Linq将执行推迟到绝对必要为止。您可以将enumerableCollection
视为要枚举工作的方式的定义,而不是单个枚举的结果。
因此,每次枚举(调用ElementAt
时所做的)都会重新枚举原始集合,并且由于您选择了随机订购,因此答案每次都不同。
您可以通过在末尾添加.ToList()
使其达到预期的效果:
var enumerableCollection = collection.OrderBy(e => random.NextDouble()).ToList();
这将执行枚举,并将结果存储在列表中。通过使用此方法,您每次枚举enumerableCollection
时都不会重新枚举原始列表。
答案 1 :(得分:4)
简单的答案是,您正在专门强制执行不确定性情况。 为什么是由于LINQ的功能。
LINQ的核心思想是构建对象,这些对象表示对一组数据(对象,记录等)的操作,然后在枚举时执行这些操作。当您调用a=A()
a.price()
print(a.name)
时,返回的对象将处理原点以执行排序,而不是已排序的新集合。顺序为仅当您实际枚举数据时-在这种情况下,通过调用OrderBy
。
每次枚举ElementAt
对象时,它将运行您指定的操作序列,从而创建元素的随机顺序。因此,每个IEnumerable<string>
调用都将返回新随机序列的第一个元素。
基本上,您可能会误解的是:ElementAt(0)
是不是集合。
不过,您可以使用IEnumerable<T>
或IEnumerable<string>
将ToArray()
转换为集合。这些将获取ToList()
指定的操作序列的输出,并分别创建一个新集合-IEnumerable<>
或string[]
。
例如:
List<string>
现在,您将从原始数组中获得一个随机项,但是它将多次成为同一项。无需每次都随机化,而只是读取顺序随机的新集合中的第一项。
答案 2 :(得分:2)
许多以前的回答在技术上都是正确的,但我认为直接查看Enumerable的实现很有用。
我们可以看到ElementAt调用GetEnumerator,这反过来产生了当前分组的迭代。
如果您不熟悉,请参阅yield.
对于给定的示例,当前分组为
e => random.NextDouble()
这意味着我们将无休止地遍历集合(查看收益的无限循环),并为针对random.NextDouble()执行的每个元素返回分组的分辨率。
因此,这里的不确定性是由于随机分组的不确定性。这是LINQ终端语句(.List(),toArray()等)的预期行为,但是如果您尝试事先使用它们,可能会造成混淆。
答案 3 :(得分:1)
我很高兴问题在这里。尽管延迟执行的概念众所周知,但是人们仍然会犯错误并多次枚举IEnumerable
,即使Eric Lippert在https://ericlippert.com/2014/10/20/producing-combinations-part-three/中也没有避免。这样做可能并最终会破坏您的代码。如果您需要多次枚举IEnumerable
,请实例化它(例如使用ToList
)。
修改
在尊重埃里克·利珀特(Eric Lippert)权威的前提下,让我展示多重枚举的糟糕程度。这是基于列表的正确结果:
Zero,One,Two
Zero,One,Three
Zero,One,Four
Zero,Two,Three
Zero,Two,Four
Zero,Three,Four
One,Two,Three
One,Two,Four
One,Three,Four
Two,Three,Four
这是当我们处理顺序不稳定的序列时发生的事情:
Three,One,Four
Four,Two,One
One,Zero,Three
One,Zero,Four
Three,Two,Zero
One,Four,Zero
Two,Four,One
Two,Three,Four
Two,Three,Four
Four,Three,One
和要点
枚举之一是对序列进行计数;一个序列 多次枚举时没有稳定计数的是 您不应该尝试将其组合成一个序列的序列!
- 计数不一定会枚举序列。
- 在该系列文章中,我强烈建议使用不可变的数据结构,该结构在枚举时总是安全且高效的 多次。