改组所需的IEnumerable扩展方法

Gul*_*han 23 c# ienumerable shuffle

我需要一个可以随机播放的扩展方法IEnumerable<T>.它还可以int指定返回的大小IEnumerable.更好地保持不变性IEnumerable.我目前的解决方案IList-

public static IList<T> Shuffle<T>(this IList<T> 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<T> Shuffle<T>(this IList<T> list)
{ return list.Shuffle(list.Count); }
Run Code Online (Sandbox Code Playgroud)

Luk*_*keH 46

你可以使用Fisher-Yates-Durstenfeld shuffle.没有必要将size参数显式传递给方法本身,Take如果不需要整个序列,可以简单地调用一个调用:

var shuffled = originalSequence.Shuffle().Take(5);

// ...

public static class EnumerableExtensions
{
    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
    {
        return source.Shuffle(new Random());
    }

    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (rng == null) throw new ArgumentNullException("rng");

        return source.ShuffleIterator(rng);
    }

    private static IEnumerable<T> ShuffleIterator<T>(
        this IEnumerable<T> 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];
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 是的,这是因为参数检查是急切地完成而不是被推迟.如果第二个和第三个方法一起滚动,则在开始迭代序列之前不会进行任何参数检查. (8认同)
  • @nawfal:许多内置的LINQ方法在内部缓冲整个序列,然后懒惰地产生结果:例如,`GroupBy`,`OrderBy`,`OrderByDescending`,`ThenBy`,`ThenByDescending`,`Reverse`等全部需要缓冲其源序列; `除了`,'GroupJoin`,`Intersect`,`Join`等都缓冲了它们的"辅助"输入序列.这不是问题,imo,尽管明确记录方法是否需要在内部缓冲整个序列是个好主意. (5认同)
  • 第二种方法的目的是抛出异常吗? (2认同)