確実なこと。LINQ でこれを行うのは少し難しいですが、標準のクエリ演算子のみを使用することで確実に可能です。
更新: これは、2010 年 6 月 28 日月曜日の私のブログの主題です。素晴らしい質問をありがとう。また、私のブログへのコメント投稿者は、私が提供したクエリよりもさらに洗練されたクエリがあることに気付きました。ここでコードを更新して使用します。
トリッキーな部分は、任意の数のシーケンスのデカルト積を作成することです。それに比べれば、文字の「Zipping」は些細なことです。これを調べて、その仕組みを確実に理解する必要があります。各パーツは十分にシンプルですが、それらを組み合わせる方法には慣れが必要です。
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 つのもののデカルト積を計算する演算子が既に LINQ にあるという事実を利用することです。
from x in xs
from y in ys
do something with each possible (x, y)
アキュムレータのデカルト積を入力シーケンスの次の項目で繰り返し取得し、結果を少し貼り付けることで、デカルト積を生成できます。
アキュムレータの値について考えてみましょう。説明のために、アキュムレータの値を、それに含まれるシーケンス演算子の結果として示します。これは、アキュムレータに実際に含まれているものではありません。アキュムレータに実際に含まれているのは、これらの結果を生成する演算子です。ここでの操作全体は、シーケンス演算子の巨大なツリーを構築するだけであり、その結果がデカルト積です。ただし、最終的なデカルト積自体は、クエリが実行されるまで実際には計算されません。 説明のために、各段階での結果を示しますが、これには実際に演算子が含まれていることを覚えておいてください。それらの結果を生み出します。
シーケンスのシーケンスのデカルト積を取得しているとします{{1, 2}, {3, 4}, {5, 6}}
。アキュムレータは、1 つの空のシーケンスを含むシーケンスとして開始します。{ { } }
最初の累積では、accumulator は { { } } であり、item は {1, 2} です。これを行います:
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] {item})
{ { } }
したがって、 withのデカルト積を取得し、{1, 2}
ペアごとに連結します: ペア({ }, 1)
があるので、連結{ }
し{1}
て を取得します{1}
。ペア({ }, 2})
があるので、 と を連結{ }
し{2}
て を取得します{2}
。したがって{{1}, {2}}
、結果として得られます。
したがって、2 番目の累積では、accumulator は{{1}, {2}}
で、item は{3, 4}
です。繰り返しますが、これら 2 つのシーケンスのデカルト積を計算して、次を取得します。
{({1}, 3), ({1}, 4), ({2}, 3), ({2}, 4)}
次に、それらの項目から、2 番目の項目を最初の項目に連結します。結果は、私たちが望むものである sequence{{1, 3}, {1, 4}, {2, 3}, {2, 4}}
です。
今、私たちは再び蓄積します。アキュムレータのデカルト積{5, 6}
を取得します
{({ 1, 3}, 5), ({1, 3}, 6), ({1, 4}, 5), ...
次に、2 番目の項目を最初の項目に連結して取得します。
{{1, 3, 5}, {1, 3, 6}, {1, 4, 5}, {1, 4, 6} ... }
これで完了です。デカルト積を蓄積しました。
これで、任意の数のシーケンスのデカルト積を取得できるユーティリティ関数ができたので、残りは比較すると簡単です。
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);
これで、一連の文字列のシーケンスができました。1 行に 1 つの文字列のシーケンスです。
foreach (var line in result)
{
foreach (var s in line)
Console.Write(s);
Console.WriteLine();
}
簡単!