使用LINQ的一组列表的笛卡尔积的两种方法

Far*_*ori 5 c# linq generics cartesian-product

我需要创建一系列列表的笛卡尔积.例如,我有:

{{4,3,7},{1,2,9},{5,8}}

我需要:{4,1,5},{4,1,8},{3,1,5},{3,1,8},......,{7,9,8}

到目前为止,我已经了解到你可以使用这样的东西:

var lists = new List<List<int>>
{ 
    new List<int> { 4, 3, 7},
    new List<int> { 1, 2, 9},
    new List<int> { 5, 8},
};

IEnumerable<IEnumerable<int>> empty = new[] { Enumerable.Empty<int>() };
var agg = lists.Aggregate(
    empty,
    (acc, next) 
=>  
    (from ac in acc
    from n in next
    select ac.Concat(new[] {n})));
Run Code Online (Sandbox Code Playgroud)

但是,最初当我实现这个时,我实现它是这样的:

var lists = new List<List<int>>
{ 
    new List<int> { 4, 3, 7},
    new List<int> { 1, 2, 9},
    new List<int> { 5, 8},
};

var agg = lists.Aggregate(
    new List<List<int>>() {new List<int> {}},
    (acc, next) 
=>  
    (from ac in acc
    from n in next
    select ac.Add(n)).ToList());
Run Code Online (Sandbox Code Playgroud)

此特定实现无法使用此错误进行编译:

在源类型为"System.Collections.Generic.List>"的查询表达式中,后续from子句中不允许使用类型为"System.Collections.Generic.List"的表达式.调用"SelectMany"时类型推断失败.

我对此错误消息感到有点困惑.我不明白为什么List不允许在那个位置?

Cya*_*ish 5

错误消息的第一部分有点误导.以下将编译并正常工作:

var agg = lists.Aggregate(
    new List<List<int>>(),
    (acc, next) 
=>  
    (from ac in acc
    from n in next
    // select ac.Add(n)).ToList());
    select ac.Concat(new[] {n}).ToList()).ToList());
Run Code Online (Sandbox Code Playgroud)

真正的问题在于select ac.Add(n).select子句必须返回一个值(在本例中为a List<int>),但是您的调用将List<T>.Add修改原始列表并返回void.

在引擎盖下,LINQ尝试将此LINQ表达式转换为(除其他外)调用Enumerable.SelectMany.编译器传递的参数之一是生成的lambda表达式返回ac.Add(n)(即void).编译器尝试根据可用的重载来推断生成的lambda的类型SelectMany,但是没有兼容的重载,因此无法确定lambda的类型.因此错误消息"调用'SelectMany'时类型推断失败.".

同样Add,大多数List<T>方法通常不适合使用LINQ进行函数式编程,这就是为什么首选解决方案仅依赖于IEnumerable<T>与LINQ相关的扩展方法.