Повторить перечислимую неопределенность

Существует ли перечислимый метод расширения, который повторяет перечислимое значение неопределенно?

Так, например, с учетом перечислимого, который возвращает: [ "a", "b", "c" ]. Мне нужен метод, который возвращает бесконечную повторяющуюся последовательность [ "a", "b", "c", "a", "b", "c", "a", "b", "c"... ]

Это похоже на Observable.Repeat, за исключением того, что я хотел бы работать с IEnumerables.

Enumerable.Repeat генерирует только перечислимый элемент из одного элемента.

Ответ 1

Я не знаю ничего, встроенного в 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, например, то любые изменения самих объектов будут по-прежнему видны.

Ответ 2

Не можете ли вы использовать Repeat + SelectMany?

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

По моему мнению, метод расширения полезен, только если вам это нужно часто. Я сомневаюсь, что вам нужно RepeatIndefinitely часто. Но a 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();

Ответ 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);
}