可能重复:
Generating all Possible Combinations
Is there a good LINQ way to do a cartesian product?
How to generate combination of N elements with limited supply of 2 each without explicit nested loops
我有一个列表列表,我想迭代所有可能的组合,我从每个内部列表中选择一个元素。如果我在编译时知道有多少列表,这是非常简单的,但是当我不预先知道将有多少列表时,我怎么能这样做呢?
如果我有三个列表(如果我知道,在编译时,将会有三个列表),并且我想要从三个列表中选择一个元素的所有组合,我可以这样做使用LINQ查询很容易:
var list1 = new[] { 1, 2 };
var list2 = new[] { 3, 4 };
var list3 = new[] { 5, 6 };
var combinations = from item1 in list1
from item2 in list2
from item3 in list3
select new[] { item1, item2, item3 };
// Results:
// {1, 3, 5}
// {1, 3, 6}
// {1, 4, 5}
// {1, 4, 6}
// {2, 3, 5}
// {2, 3, 6}
// {2, 4, 5}
// {2, 4, 6}
但是当我在编译时不知道会有多少列表时,我怎么能做同样的事情呢?
var lists = new[] {
new[] { 1, 2 },
new[] { 3, 4 },
new[] { 5, 6 } };
var combinations = ???;
// This particular example happens to be the same inputs as above, so it
// has the same expected outputs. But there could be two lists instead,
// or four, so the three hard-coded "from" clauses won't work.
看起来这应该在LINQ中实际可行 - SelectMany已经相当于两个嵌套的foreach循环,所以我需要做的就是做一堆SelectMany调用然后将所有结果与另一个SelectMany结合起来。或者其他的东西。但是当它开始变得那样的时候,我的大脑就会被打结。我无法理解如何将各个部分组合在一起。我甚至无法弄清楚外部SelectMany调用的泛型类型参数是什么。
如何迭代这些列表列表,并返回所有组合,而不知道在编译时会有多少列表?
(注意:无论我在上面使用过哪些数组,我都可以使用IEnumerable<T>
代替。在示例代码中,数组更容易编写,但我希望输出更可能在形成IEnumerable<IEnumerable<int>>
而不是我在上面的示例输出中显示的int[][]
。)
答案 0 :(得分:2)
您不使用SelectMany来组合SelectMany调用;你使用Aggregate。代码由Eric Lippert提供(回答一个比这一个更具体的问题,但给出一个符合这个问题的一般答案):
static IEnumerable<IEnumerable<T>> CartesianProduct<T>(
this IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] {item}) :
);
}
正如Eric的所有答案一样,他包含一个detailed discussion,它根据等效的非LINQ代码确定了其工作原理和原因。