Я пытаюсь записать оператор LINQ, который возвращает меня все возможные комбинации чисел (мне нужно это для теста, и я был вдохновлен этой статьей Eric Lippert). Прототип метода, который я называю, похож:
IEnumerable> AllSequences( int start, int end, int size );
Правила:
size
start
и end
должен использоватьсяТак вызов AllSequences( 1, 5, 3 )
должен привести к 10 наборам, каждому размеру 3:
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
Теперь, так или иначе я действительно хотел бы видеть чистое решение LINQ. Я могу записать не решение LINQ самостоятельно, поэтому не приложите усилия к решению без LINQ.
Мои попытки до сих пор закончились в точке, где я должен присоединиться к числу с результатом рекурсивного вызова моего метода - что-то как:
return from i in Enumerable.Range( start, end - size + 1 )
select BuildCollection(i, AllSequences( i, end, size -1));
Но я не могу управлять им для реализации BuildCollection()
на основе LINQ - или даже пропускают этот вызов метода. Можно ли помочь мне здесь?
Думаю, я понял.
IEnumerable<List<int>> AllSequences(int start, int end, int size)
{
if (size == 0)
return Enumerable.Repeat<List<int>>(new List<int>(), 1);
return from i in Enumerable.Range(start, end - size - start + 2)
from seq in AllSequences(i + 1, end, size - 1)
select new List<int>{i}.Concat(seq).ToList();
}
Думаю, что-то вроде следующего должно сработать.
public static IEnumerable<IEnumerable<int>> AllSequences(int start, int end,
int size)
{
return size <= 0 ? new[] { new int[0] } :
from i in Enumerable.Range(start, end - size - start + 2)
from seq in AllSequences(i + 1, end, size - 1)
select Enumerable.Concat(new[] { i }, seq);
}
Ключом к решению является соединение из
раздела , которое очень удобно для работы с вложенными перечислимыми элементами.
Обратите внимание, что я немного изменил сигнатуру метода на IEnumerable
, поскольку это более удобно при использовании (чистого) LINQ. Однако вы всегда можете легко преобразовать его в IEnumerable
в конце, если хотите.
Сообщите мне, нуждается ли код в каких-либо пояснениях, но я надеюсь, что синтаксис LINQ проясняет его.
Редактировать 1: Исправлена ошибка и улучшена лаконичность.
Правка 2:
Поскольку мне скучно и мне больше нечего делать (нет, не совсем), я подумал, что напишу метод расширения, который вычисляет комбинации заданного списка элементы, используя метод AllSequences
.
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IList<T> source,
int num)
{
return AllSequences(0, source.Count - 1, num).Select(
seq => seq.Select(i => source[i]));
}
Возможно, не самый эффективный способ вычисления комбинаций, но, безусловно, довольно компактный код!