Повторить перечислимую неопределенность
Существует ли перечислимый метод расширения, который повторяет перечислимое значение неопределенно?
Так, например, с учетом перечислимого, который возвращает: [ "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);
}