【问题标题】:How can I deal with multiple foreach loops when I'm working with multiple data types处理多种数据类型时如何处理多个 foreach 循环
【发布时间】:2015-07-27 14:51:47
【问题描述】:

我有一个问题困扰了我一段时间,它与我的程序中循环的成倍增长有关。我将让下面的代码进行对话并在其中添加 cmets。

void Main()
{
    //Here we are just creating simple lists
    List<string> strings = new List<string>();
    strings.Add("a");
    strings.Add("b");
    strings.Add("c");

    List<int> integers = new List<int>();
    integers.Add(1);
    integers.Add(2);
    integers.Add(3);

    //Creating complex classes ( not really )
    ComplexClass cc1 = new ComplexClass();
    cc1.CCString = "A test";
    cc1.CCInt = 2;

    ComplexClass cc2 = new ComplexClass();
    cc2.CCString = "Another test";
    cc2.CCInt = 6;

    //Creating a list of these too
    List< ComplexClass > complexClasses = new List< ComplexClass >();
    complexClasses.Add(cc1);
    complexClasses.Add(cc2);


    //Here i want to create every possible combination using each of the lists 
    //and then add these to a testData class to do other things with, serialize, save, print etc.
    //The main question is here, the for loops will definitely increase exponentially with each
    //list added to. 
    foreach( int i in integers )
    {
        foreach( string s in strings )
        {
            foreach( ComplexClass compClass in complexClasses )
            {
                TestData data = new TestData();
                data.TestInteger = i;
                data.TestString = s;
                data.TestComplexClass = compClass;

                OutPutTestData( data );
            }
        }
    }
}

//Simply outputs the data as test but I will be keeping the object for later also
public void OutPutTestData( TestData testData )
{
    Console.WriteLine( testData.TestString + testData.TestInteger + testData.TestComplexClass.CCString );
}

//The "Complex class" again not that complex but an example of what im tring to achieve
public class ComplexClass
{
    public string CCString{ get; set; }
    public int CCInt { get; set; }
}

//The overall test object which holds multiple properties of different data types
public class TestData
{
    public string TestString { get; set; }
    public int TestInteger { get; set; }
    public ComplexClass TestComplexClass { get; set; }
}

输出

a1 测试1

a1 测试2

b1 测试1

b1 测试2

c1 测试1

c1 测试2

a2 测试1

a2 测试2

b2 测试1

b2 测试2

c2 测试1

c2 测试2

a3 测试1

a3 测试2

b3 测试1

b3 测试2

c3 测试1

c3 测试2

如您所见,循环有效,并为我提供了所提供数据的所有可能组合。

我的问题是随着我添加更多列表,for 循环呈指数增长。可能有大量列表。

我知道迭代次数会随着组合的发现而增加,这不是问题,因为我计划在估计可能的总迭代次数后,根据用户输入以编程方式限制可能发生的迭代次数。

例如总迭代次数为 234,因此仅迭代 120 次(120 种组合)

提供的代码非常适合嵌套的 foreach 循环,但随着它呈指数增长,它变得难以阅读、难以管理并且通常不美观。

我看过这样的置换算法:

Algorithm to generate all possible permutations of a list?

Understanding Recursion to generate permutations.

但它们只允许使用一种特定的数据类型而不是多种。

我还研究了笛卡尔积,但我发现的唯一示例仅涉及一种数据类型。

【问题讨论】:

  • 最终游戏到底是什么?找到Lists的每一个可能的组合?
  • 我想我不确定问题到底是什么。无论你做什么,组合的数量都会成倍增长,除非我错过了什么。
  • @Servy 你愿意提供一个你认为应该如何工作的解决方案吗?
  • 你看LINQ and N-ary Cartesian Products 和后续行动了吗?
  • @Servy 您提出的解决方案将添加两个强制转换并且仍然需要额外的代码来维护,但如果您对此有强烈的感觉,请继续。我不会重新打开它。

标签: c# loops


【解决方案1】:

即使你选择了一个答案,我想你可能想看看这个......使用递归,你所要做的就是把你所有的Lists 放在一个List&lt;IList&gt; 中。您只需将任何新添加的Lists 添加到List&lt;IList&gt;

我在您的ComplexClass 中添加了一个override ToString() 以使其适合。

        public static void Test()
        {
            //Here we are just creating simple lists
            List<string> strings = new List<string>();
            strings.Add("a");
            strings.Add("b");
            strings.Add("c");

            List<int> integers = new List<int>();
            integers.Add(1);
            integers.Add(2);
            integers.Add(3);

            //Creating complex classes ( not really )
            ComplexClass cc1 = new ComplexClass();
            cc1.CCString = "A test";
            cc1.CCInt = 2;

            ComplexClass cc2 = new ComplexClass();
            cc2.CCString = "Another test";
            cc2.CCInt = 6;

            //Creating a list of these too
            List<ComplexClass> complexClasses = new List<ComplexClass>();
            complexClasses.Add(cc1);
            complexClasses.Add(cc2);

            // NEW LIST
            List<double> doubles = new List<double>();
            doubles.Add(99.99);
            doubles.Add(100.12);

            List<IList> myLists = new List<IList> {integers, strings, complexClasses, doubles};
            Permutate("", myLists, 0);

            Console.ReadLine();
        }

        public static void Permutate(string s, List<IList> list, int i)
        {
            if (i == list.Count)
            {
                Console.WriteLine(s);
            }
            else
            {
                foreach (object obj in list[i])
                {
                    Permutate(s + obj + " ", list, i + 1);
                }
            }
        }

        //The "Complex class" again not that complex but an example of what im tring to achieve
        public class ComplexClass
        {
            public string CCString { get; set; }
            public int CCInt { get; set; }

            // Added override
            public override string ToString()
            {
                return CCString + CCInt;
            }
        }

结果(并非所有结果都被捕获):

【讨论】:

  • 这实际上是我想要的。做得好,感谢您的回答。我认为减少引用列表的次数对于这个问题至关重要。
  • 我已经开始研究委托、回调和这类东西。我知道有可能以某种方式传递一个方法,所以你不会耦合到 toString 方法,对吧?
【解决方案2】:

您可以通过在 Linq 中进行交叉连接来摆脱 for 循环:

var query = 
    from  i in integers
    from s in strings 
    from compClass in complexClasses
    select new TestData()
    {
        TestInteger = i,
        TestString = s,
        TestComplexClass = compClass
    };

foreach (var data in query)
    OutPutTestData( data );

如果列表都是同一类型,那么您可以构建一个交叉连接不同数量列表的查询。在您的情况下,由于列表的类型不同,这是不可能的(没有反射、动态或更丑陋的东西)

【讨论】:

  • 这与 OP 的解决方案并没有真正的不同。
  • 但我有一种感觉,可读性更强,更容易更改,至少对于 C#“演讲者”而言
  • @MVCDS 您认为 3 个 foreach 循环是大多数 C# 程序员无法理解的吗?我会质疑这一点。
  • @Servy 在功能上完全一样,但添加嵌套 for 循环更简洁。我仍然不确定实际问题是什么(或者这是否能解决任何问题),但它是一种替代方案。
  • @Servy 我认为这实际上可能需要更多的工作......因为您可以在new TestData() 中添加一个新的初始化程序,而OutPutTestData 可能会忽略它(除非您记得更新它并留下良好的文档记录),从而产生假阳性测试结果。有办法解决这个问题,但我同意你最终还是会在 3 个不同的地方更新代码。因此,在这一点上,这只是风格问题 - 在嵌套了多少层 for 循环之后,由于代码缩进,它变得难以阅读......
猜你喜欢
  • 1970-01-01
  • 2015-05-29
  • 2018-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多