无限重复一次可枚举

时间:2014-06-12 10:03:04

标签: c# linq

是否有可枚举的扩展方法无限期地重复可枚举?

例如,给定一个可以返回的枚举:[" a"," b"," c"]。我想要一个返回无限重复序列的方法[" a"," b"," c"," a",&# 34; b"," c"," a"," b"," c" ......]

这听起来有点像Observable.Repeat,除了我想在IEnumerables上操作。

Enumerable.Repeat仅从单个元素生成可枚举。

6 个答案:

答案 0 :(得分:19)

我不知道LINQ中内置的任何内容,但 很容易创建自己的内容:

public static IEnumerable<T> RepeatIndefinitely<T>(this IEnumerable<T> source)
{
    while (true)
    {
        foreach (var item in source)
        {
            yield return item;
        }
    }
}

请注意,这会多次评估source - 可能只想这样做一次,创建副本:

public static IEnumerable<T> RepeatIndefinitely<T>(this IEnumerable<T> source)
{
    var list = source.ToList();
    while (true)
    {
        foreach (var item in list)
        {
            yield return item;
        }
    }
}

注意:

  • 创建序列的副本意味着可以自由修改原始序列,而不必担心此代码会同时迭代它。
  • 创建序列的副本意味着它需要足够小以适应内存,当然。这可能不太理想。
  • 这只会在您开始迭代结果时创建一个副本。这很容易令人惊讶。另一种方法是使用非迭代器方法创建副本,然后委托给私有迭代器方法。这是LINQ中用于参数验证的方法。
  • 副本很浅 - 例如,如果源是一系列StringBuilder引用,那么对象本身的任何更改仍然可见。

答案 1 :(得分:6)

你不能使用Repeat + SelectMany吗?

var take100ABC = Enumerable.Repeat(new[] { "A", "B", "C" }, 100)
                           .SelectMany(col => col);

在我看来,扩展方法只有在经常需要时才有用。我怀疑你经常需要RepeatIndefinitely。但在许多情况下,RepeatWhile可能很方便。你也可以无限重复。

所以这是我的第一次尝试:

public static IEnumerable<TSource> RepeatWhile<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    TSource item = default(TSource);
    do
    {
        foreach (TSource current in source)
        {
            item = current;
            yield return item;
        }
    }
    while (predicate(item));
    yield break;
}

您可以将它用于“无限”重复,例如以这种方式:

string[] collection = { "A", "B", "C"};
var infiniteCollection = collection.RepeatWhile(s => s == s);
List<string> take1000OfInfinite = infiniteCollection.Take(1000).ToList();

答案 2 :(得分:3)

您可以创建一个简单的RepeatForever扩展名并在序列上使用它,然后在序列序列上使用SelectMany来展平它。

public static IEnumerable<T> RepeatForever<T>(this T item)
{
    for(;;) yield return item;
}
public static IEnumerable<T> RepeatSequenceForever<T>(this IEnumerable<T> seq)
{
    return seq.RepeatForever().SelectMany(x => x);
}

答案 3 :(得分:2)

如果您可以使用System.Interactive (aka Ix)的NuGet软件包,而仅使用Repeat(),这是另一种选择。

var sequence = Enumerable.Range(1, 3).Repeat();

foreach (var item in sequence.Take(10))
{
    Console.WriteLine(item); // 1, 2, 3, 1, 2, 3, 1, 2, 3, 1
}

答案 4 :(得分:0)

如果您不介意额外的依赖,您可以使用 Repeat

中的 MoreLinq 方法

它完全符合您的需求。 例如:

    var sequence = new[] {"a", "b", "c"};
    var generator = sequence.Repeat();
    
    var boundSequence = Enumerable.Range(0,10).Zip(generator);
    
    foreach (var (i,val) in boundSequence)
    {
        Console.WriteLine($"{i}: {val}");
    }

打印:

    0: a
    1: b
    2: c
    3: a
    4: b
    5: c
    6: a
    7: b
    8: c
    9: a

答案 5 :(得分:-1)

与已接受的答案相同的解决方案,但我想避免在空的枚举数上进行致命的旋转,或者避免产生耗尽它的副作用。

public static IEnumerable<T> RepeatIndefinitely<T>(this IEnumerable<T> source) {
    var t = source.GetEnumerator();
    while (t.MoveNext()) {
        do {
            yield return t.Current;
        } while (t.MoveNext());
        t = source.GetEnumerator();
    }
    throw new InvalidOperationException("Sequence contains no elements, possibly after reset.");
}