3

ここでのEricLippertの投稿は、私が抱えている特定の問題に適していることがわかりました。

問題は、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})                       
        );
 }

2つのコレクションに対するEricの例を次に示します。

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>であるため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 に答える