3

我发现 Eric Lippert 的帖子适合我遇到的一个特殊问题。

问题是我无法理解我应该如何将它与 2+ 数量的集合一起使用。

var collections = new List<List<MyType>>();
foreach(var item in somequery)
{
    collections.Add(
            new List<MyType> { new MyType { Id = 1} .. n }
        );
}

如何在集合变量上应用笛卡尔积 linq 查询?

扩展方法是这样的:

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 的 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);
4

3 回答 3

5

示例代码已经能够处理“n”个笛卡尔积(在示例中它是 3)。你的问题是List<List<MyType>>当你需要一个IEnumerable<IEnumerable<MyType>>

IEnumerable<IEnumerable<MyType>> result = collections
  .Select(list => list.AsEnumerable())
  .CartesianProduct();
于 2012-11-30T20:22:56.630 回答
1

就像这个问题的原始海报一样,我也很难理解这个很棒的功能的使用。吸引我的主要事情是我必须在调用函数之前创建这个IEnumerable完整的单曲IEnumerable(再次就像原始帖子一样)。

我的代码设置方式是我有 3 个数组,其中包含我需要相乘的数据,而创建这个更大的数组IEnumerable是我的方式,我不想这样做。

因此,我重新编写了扩展函数,IEnumerable<T>而不是IEnumerable<IEnumerable<T>>现在我可以直接从我想要相乘的任何数组中调用该函数,并将其他 2 个数组作为参数传递。我想我会在这里重新发布,以防其他人有兴趣以这种方式使用它:

    public static IEnumerable<IEnumerable<T>> CartesianProduct<T>
    (this IEnumerable<T> firstSequence, params IEnumerable<T>[] sequences)
    {
        IEnumerable<IEnumerable<T>> result = new[] { Enumerable.Empty<T>() };

        foreach (IEnumerable<T> sequence in (new[] { firstSequence }).Concat(sequences))
        {
            result = from resultItem in result 
                     from sequenceItem in sequence 
                     select resultItem.Concat(new[] { sequenceItem });
        }
        return result;
    }

这将是在 3 个数据数组上使用此方法的示例。

        var numbers = new object[] { 1, 2, 3 };
        var letters = new object[] { "a", "b", "c" };
        var colors = new object[] { "red", "blue", "yellow" };

        var result = numbers.CartesianProduct(letters, colors);

输出

        [1, a, red]
        [1, a, blue]
        [1, a, yellow]
        [1, b, red]
        [1, b, blue]
        [1, b, yellow]
        [1, c, red]
        [1, c, blue]
        [1, c, yellow]
        [2, a, red]
        [2, a, blue]
        [2, a, yellow]
        [2, b, red]
        [2, b, blue]
        [2, b, yellow]
        [2, c, red]
        [2, c, blue]
        [2, c, yellow]
        [3, a, red]
        [3, a, blue]
        [3, a, yellow]
        [3, b, red]
        [3, b, blue]
        [3, b, yellow]
        [3, c, red]
        [3, c, blue]
        [3, c, yellow]
于 2021-07-09T03:56:59.633 回答
0

由于List<T>is IEnumerable<T>,那么您使用 Eric 解决方案的问题解决如下:

var collections = new List<List<MyType>>();
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);
}
于 2017-09-20T16:22:14.407 回答