简体   繁体   English

使用LINQ生成数字序列

[英]Generate number sequences with LINQ

I try to write a LINQ statement which returns me all possible combinations of numbers (I need this for a test and I was inspired by this article of Eric Lippert ). 我尝试编写一个LINQ语句,它返回所有可能的数字组合(我需要这个测试,我的灵感来自Eric Lippert的这篇文章 )。 The method's prototype I call looks like: 我调用的方法原型如下:

IEnumerable<Collection<int>> AllSequences( int start, int end, int size );

The rules are: 规则是:

  • all returned collections have a length of size 所有返回的集合都有一个size的长度
  • number values within a collection have to increase 集合中的数字值必须增加
  • every number between start and end should be used 应该使用startend之间的每个数字

So calling the AllSequences( 1, 5, 3 ) should result in 10 collections, each of size 3: 所以调用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

Now, somehow I'd really like to see a pure LINQ solution. 现在,不知怎的,我真的很想看到纯粹的LINQ解决方案。 I am able to write a non LINQ solution on my own, so please put no effort into a solution without LINQ. 我可以自己编写非LINQ解决方案,所以请不要在没有LINQ的情况下投入解决方案。
My tries so far ended at a point where I have to join a number with the result of a recursive call of my method - something like: 到目前为止,我的尝试已经结束,我必须加入一个数字与我的方法的递归调用的结果 - 如下所示:

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

But I can't manage it to implement BuildCollection() on a LINQ base - or even skip this method call. 但我无法管理它在LINQ基础上实现BuildCollection() - 甚至跳过此方法调用。 Can you help me here? 你能帮帮我吗?

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

Think I've got it. 我想我已经知道了。

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

Something like the following should do the job, I think. 我认为,像下面这样的事情应该可以胜任。

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

The key to the solution is the compound from clause , which is quite handy for dealing with nested enumerables. 解决方案的关键是复合from子句 ,这对于处理嵌套的可枚举非常方便。

Notice that I've changed the method signature slightly to IEnumerable<IEnumerable<int>> , since this is more convenient when using (pure) LINQ. 请注意,我已将方法签名稍微更改为IEnumerable<IEnumerable<int>> ,因为使用(纯)LINQ时更方便。 You can always convert it easily to a IEnumerable<ICollection<int>> at the end if you like, however. 但是,如果您愿意,可以随时将其轻松转换为IEnumerable<ICollection<int>>

Let me know if the code needs any explanation, but I'm hoping the LINQ syntax makes it reasonably clear. 如果代码需要任何解释,请告诉我,但我希望LINQ语法能够让它变得相当清楚。

Edit 1: Fixed bug and improved conciseness. 编辑1:修复了错误并提高了简洁性。

Edit 2: Because I'm bored and have nothing better to do (no, not really), I thought I'd write an extension method that compute the combinations of a given list of elements, making use of the AllSequences method. 编辑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]));
}

Perhaps not the most efficient way of computing combinations, but certainly pretty compact code! 也许不是最有效的计算组合方式,但肯定是非常紧凑的代码!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM