我尝试编写一个LINQ语句,它返回所有可能的数字组合(我需要这个测试,我受到了这个article of Eric Lippert的启发)。我调用的方法原型如下:
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
现在,不知怎的,我真的很想看到纯LINQ 解决方案。我可以自己编写一个非LINQ解决方案,所以请不要在没有LINQ的情况下投入解决方案 到目前为止,我的尝试已经结束,我必须使用我的方法的递归调用的结果加入一个数字 - 例如:
return from i in Enumerable.Range( start, end - size + 1 )
select BuildCollection(i, AllSequences( i, end, size -1));
但我无法管理它在LINQ基础上实现BuildCollection()
- 甚至跳过此方法调用。你能帮帮我吗?
答案 0 :(得分:50)
Enumerable.Range(1, 12)
.Select(x => (x - 1) + 1);
答案 1 :(得分:34)
想想我已经得到了它。
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 :(得分:13)
我想,像下面这样的事情应该可以胜任。
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);
}
解决方案的关键是compound from
clause,这对于处理嵌套的可枚举非常方便。
请注意,我已将方法签名稍微更改为IEnumerable<IEnumerable<int>>
,因为使用(纯)LINQ时更方便。但是,如果您愿意,可以随时将其轻松转换为IEnumerable<ICollection<int>>
。
如果代码需要任何解释,请告诉我,但我希望LINQ语法能够让它变得相当清楚。
编辑1:修正了错误并提高了简洁性。
编辑2:
因为我很无聊而且没有更好的事情要做(不,不是真的),我想我会编写一个扩展方法来计算给定元素列表的组合,并使用AllSequences
方法。 / p>
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]));
}
也许不是最有效的计算组合方式,但肯定是非常紧凑的代码!