关于洗刷所需的IEnumerable的扩展方法
我需要一个扩展IEnumerable
的扩展方法。 它还可以使用int
来指定返回的IEnumerable
的大小。 更好地保持IEnumerable
不变性。 我目前的IList
解决方案 –
public static IList Shuffle(this IList list, int size) { Random rnd = new Random(); var res = new T[size]; res[0] = list[0]; for (int i = 1; i < size; i++) { int j = rnd.Next(i); res[i] = res[j]; res[j] = list[i]; } return res; } public static IList Shuffle(this IList list) { return list.Shuffle(list.Count); }
你可以使用Fisher-Yates-Durstenfeld shuffle 。 没有必要将size参数显式传递给方法本身,如果不需要整个序列,只需调用Take
:
var shuffled = originalSequence.Shuffle().Take(5); // ... public static class EnumerableExtensions { public static IEnumerable Shuffle (this IEnumerable source) { return source.Shuffle(new Random()); } public static IEnumerable Shuffle (this IEnumerable source, Random rng) { if (source == null) throw new ArgumentNullException("source"); if (rng == null) throw new ArgumentNullException("rng"); return source.ShuffleIterator(rng); } private static IEnumerable ShuffleIterator ( this IEnumerable source, Random rng) { var buffer = source.ToList(); for (int i = 0; i < buffer.Count; i++) { int j = rng.Next(i, buffer.Count); yield return buffer[j]; buffer[j] = buffer[i]; } } }
有一些LINQ的爱:
public static IEnumerable Shuffle (this IEnumerable list, int size) { var r = new Random(); var shuffledList = list. Select(x => new { Number = r.Next(), Item = x }). OrderBy(x => x.Number). Select(x => x.Item). Take(size); // Assume first @size items is fine return shuffledList.ToList(); }
安东得到了这个想法,但你可以把它变成两个class轮:
public static IEnumerable Shuffle (this IEnumerable enumerable) { var r = new Random(); return enumerable.OrderBy(x=>r.Next()).ToList(); }
不幸的是,它不能被懒惰地评估,因为r
在执行时将超出范围。 您可以创建一个封装此代码并返回该代码的IEnumerable实现,但这会变得更复杂。