Подтвердить что ты не робот

Сгенерировать числовые последовательности с помощью LINQ

Я пытаюсь написать оператор LINQ, который возвращает мне все возможные комбинации чисел (мне это нужно для теста, и я был вдохновлен этой статьей Эрика Липперта). Прототип метода, который я вызываю, выглядит так:

IEnumerable<Collection<int>> 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

Теперь мне как-то хотелось бы увидеть решение pure LINQ. Я могу написать решение, отличное от LINQ, самостоятельно, поэтому не прилагайте никаких усилий к решению без LINQ.
Мои попытки до сих пор заканчивались в точке, где мне нужно присоединиться к числу с результатом рекурсивного вызова моего метода - что-то вроде:

return from i in Enumerable.Range( start, end - size + 1 )
       select BuildCollection(i, AllSequences( i, end, size -1));

Но я не могу управлять им, чтобы реализовать BuildCollection() в базе LINQ - или даже пропустить этот вызов метода. Вы можете мне помочь?

4b9b3361

Ответ 1

Думаю, что у меня есть.

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();
}

Ответ 2

 Enumerable.Range(1, 12)
           .Select(x => (x - 1) + 1);

Ответ 3

Мне кажется, что-то вроде следующего должно выполнять эту работу.

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);
}

Ключом к решению является составная from статья, что весьма удобно для работы с вложенными перечислениями.

Обратите внимание, что я немного изменил сигнатуру метода на IEnumerable<IEnumerable<int>>, так как это более удобно при использовании (чистой) LINQ. Вы всегда можете легко преобразовать его в IEnumerable<ICollection<int>> в конце, если хотите.

Сообщите мне, если код нуждается в каких-либо объяснениях, но я надеюсь, что синтаксис 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]));
}

Возможно, не самый эффективный способ вычисления комбинаций, но, безусловно, довольно компактный код!