Python の itertools.product を C# で作る(3 引数まで対応)

Python の itertools モジュールには product という関数が定義されています。

たとえば 1, 3, 4 の数字を使って 4 桁の数を作るには、Python では itertools.product([1, 3, 4], repeat=4) と書くだけで実現できます。

配列を用意してバックトラックで桁を埋めていく、という処理が不要になるのは素晴らしいですね。

C# で product 関数を作ってみました(ただし、引数の要素数は 3 までの制限があります)。

using System;
using System.Collections.Generic;
using System.Linq;

class Program {
    static IEnumerable<T[]> Product_1<T>(int p, T[] buf, List<List<T>> pools) {
        if (p == buf.Length) {
            yield return buf;
        }
        else {
            foreach (var e in pools[p]) {
                buf[p] = e;
                foreach (var ret in Product_1(p+1, buf, pools)) {
                    yield return ret;
                }
            }
        }
    }

    static IEnumerable<T[]> Product<T>(IEnumerable<T> xs, int repeat = 1) {
        var pools = new List<List<T>>(Enumerable.Repeat(xs.ToList(), repeat));
        return Product_1(0, new T[pools.Count], pools);
    }

    static IEnumerable<T[]> Product<T>(IEnumerable<T> xs, IEnumerable<T> ys, int repeat = 1) {
        var ls = new List<List<T>> { xs.ToList(), ys.ToList() };
        var pools = new List<List<T>>(Enumerable.Repeat(ls, repeat).SelectMany(e => e));
        return Product_1(0, new T[pools.Count], pools);
    }

    static IEnumerable<T[]> Product<T>(IEnumerable<T> xs, IEnumerable<T> ys, IEnumerable<T> zs, int repeat = 1) {
        var ls = new List<List<T>> { xs.ToList(), ys.ToList(), zs.ToList() };
        var pools = new List<List<T>>(Enumerable.Repeat(ls, repeat).SelectMany(e => e));
        return Product_1(0, new T[pools.Count], pools);
    }

    static void Display<T>(IEnumerable<T[]> xs) {
        var ys = xs.Select(e => string.Join("", e));
        Console.WriteLine(string.Join(" ", ys));
        Console.WriteLine();
    }

    static void Main() {
        // 1, 2, 3 の数字を使って、2 桁の数をつくる
        Display(Product(new[] { 1, 2, 3 }, 2));

        // { 1, 2, 3 } と { 4, 5 } を組み合わせて数字をつくる
        Display(Product(new[] { 1, 2, 3 }, new[] { 4, 5 }));

        // 1, 3, 4 の数字を使って 3 桁の数をつくる
        Display(Product(new[] { 1, 3, 4 }, 3));
   }
}

実行結果です。

11 12 13 21 22 23 31 32 33

14 15 24 25 34 35

111 113 114 131 133 134 141 143 144 311 313 314 331 333 334 341 343 344 411 413 414 431 433 434 441 443 444