生成n-ary Cartesian产品示例
我发现Eric Lippert的post适合我遇到的一个特殊问题。
问题是我无法理解我应该如何使用2个以上的集合。
有
var collections = new List<List>(); foreach(var item in somequery) { collections.Add( new List { new MyType { Id = 1} .. n } ); }
如何在变量集合中应用笛卡尔积linq查询?
扩展方法是这样的:
static IEnumerable<IEnumerable> CartesianProduct(this IEnumerable<IEnumerable> sequences) { IEnumerable<IEnumerable> emptyProduct = new[] { Enumerable.Empty()}; return sequences.Aggregate( emptyProduct, (accumulator, sequence) => from accseq in accumulator from item in sequence select accseq.Concat(new[] {item}) ); }
这是Eric的2个集合的例子:
var arr1 = new[] {"a", "b", "c"}; var arr2 = new[] { 3, 2, 4 }; var result = from cpLine in CartesianProduct( from count in arr2 select Enumerable.Range(1, count)) select cpLine.Zip(arr1, (x1, x2) => x2 + x1);
示例代码已经能够执行“n”笛卡尔积(在示例中它为3)。 您的问题是当您需要IEnumerable
时,您有一个List
>
IEnumerable
IEnumerable> result = collections .Select(list => list.AsEnumerable()) .CartesianProduct();
由于List
是IEnumerable
,因此使用Eric解决方案的问题解决如下:
var collections = new List>(); var product = collections.CartesianProduct(); foreach(var collection in product) { // a single collection of MyType items foreach(var item in collection) { // each item of type MyType within a collection Console.Write(item); } }
当然,您可以以更简洁的方式聚合每个集合中的项目,例如作为单个string
:
var product = collections .CartesianProduct() .Select(xs => xs.Aggregate(new StringBuilder(), (sb, x) => sb.Append(x.ToString()), sb => sb.ToString())); foreach(var collectionAsString in product) { Console.WriteLine(collectionAsString); }