FOR循环工作很糟糕

时间:2013-05-10 15:07:13

标签: c#

这是我的问题: 我正在研究玩家石斑鱼(它将玩家分成小组)。我是通过一个for循环来做的,但它不会划分所有玩家:(。

以下是代码:

namespace Grouper
{
public partial class Form1 : Form
{
    List<string> players=new List<string>(); 

    public Form1()
    {
        InitializeComponent();
        LoadPlayers();
    }

    private void But_rnd_Click(object sender, EventArgs e)
    {
        LoadPlayers();
        bool isOdd = players.Count % 2 == 1;
        List<string> results=new List<string>();
        if(!isOdd) // Count of players is even
        {
            Grouping(ref results);
        }
        if(isOdd) // Count of players is odd
        {
            Grouping(ref results);
            results.Add("Remained: " + players[0]);
            ShowResults(ref results);
        }
    }

    private void Grouping(ref List<string> results)
    {
        Random r=new Random();
        for (int i = 0; i < players.Count() / 2 + 1; i++)
        {
            int randomPlr = r.Next(players.Count() / 2 + 1, players.Count());
            results.Add(i + 1 + ".: " + players[i] + " + " + players[randomPlr]);
            players.RemoveAt(i);
            players.RemoveAt(randomPlr - 1);
        }
    }

    private void ShowResults(ref List<string> results)
    {
        string write = "";
        foreach (string result in results)
        {
            write += result + "\n";
        }
        MessageBox.Show(write);
    }

    private void LoadPlayers()
    {
        players.Clear();
        players.Add("p1");
        players.Add("p2");
        players.Add("p3");
        players.Add("p4");
        players.Add("p5");
        players.Add("p6");
        players.Add("p7");
    }
}
}

方法ShowResults()只显示2组和1名玩家,剩下2组和1名剩余= 5名玩家,但我有7名玩家!。

6 个答案:

答案 0 :(得分:4)

您的代码中存在多个问题。

if语句使用

这实际上是奇数

bool isOdd = players.Count % 2 == 1;
…
if(!isOdd)
{
    …
}
if(isOdd) // Count of players is odd
{
    …
}

使用if / else,您的代码将更具可读性:

if (players.Count % 2 == 0)
{
    …
}
else
{
    …
}

此外,您可以完全省略此检查,进行分组,然后弄清楚如何处理其余部分(谁说每次最多会留下一个?如果您需要将它们分成三组之间怎么办?未来?):

Grouping(…);
if (players.Count > 0)
{
    … process remaining players …
}

for循环中的迭代次数

正如其他人所指出的那样,你在这里“过分”:

for (int i = 0; i < players.Count() / 2 + 1; i++)

这将总是比你想要的多两次迭代。你应该改变它:

for (int i = 1; i < players.Count() / 2; i++)

这样它可以用于偶数或奇数,也适用于角落情况(仅限0或1个玩家)。或者,如果您在每次迭代时从列表中取出两名玩家,只需使用:

while (Players.count() > 1)

您的索引和随机访问组合错误

在以下代码中,您无法确定a)i - 元素是否存在,b)随机索引与i不匹配,这将有效地将同一个玩家放入基团:

int randomPlr = r.Next(players.Count() / 2 + 1, players.Count());
results.Add(i + 1 + ".: " + players[i] + " + " + players[randomPlr]);
players.RemoveAt(i);
players.RemoveAt(randomPlr - 1);

要么占一半,那么另一半占据players列表(在这种情况下,你根本不需要做任何for个),每次随机挑选一名玩家。例如,像这样:

public string PickRandomPlayer(List<string> players)
{
    int random = … generate random index …;
    string player = players[random];
    players.RemoveAt(random);
    return player;
}

然后在for看起来两次调用此方法,为每一半选择一个玩家。

ref参数

的使用无效

以下声明包含不必要的不需要的 ref

private void Grouping(ref List<string> results)

简单地说:默认情况下,对象,如List<string> 通过引用传递。这意味着,当您访问代码中的results参数并对其进行修改(添加/删除项目)时,受影响的实例就是调用者提供的实例:

void Grouping(List<string> results) { … }

…

List<string> results = new … ;
…
Grouping(results);
…
… here results contains what Grouping put in

另一方面,当您指定ref时,您可以将方法List<string>的新实例传递出方法:

void Grouping(List<string> results)
{
    results = new … ; // this instance will be returned out of the method!
}

…

List<string> results = new … ;
…
Grouping(ref results); // here, whatever is in results currently, is lost
…

使用goto

这只是糟糕。请参阅此SO question及其接受的答案。此外,谷歌对“GOTO声明被认为有害”以进一步阅读此主题。

答案 1 :(得分:2)

循环的条件是i < players.Count() / 2 + 1。 这就是说,因为你在循环的每次迭代中删除了2个玩家,你就改变了条件的值。

假设你从7名球员开始。

  1. 迭代1:i = 0,条件= 4,2名玩家共处理,剩下5名玩家
  2. 迭代2:i = 1,条件= 3,总共处理了4名玩家,剩下3名玩家
  3. 迭代3:i = 2,条件= 2,条件不满足
  4. 最后,你有2个小组和3个剩余玩家,但是由于你"Remained: " + players[0],你只显示其余1个玩家。

答案 2 :(得分:0)

这不是很好,但应该有用(只需输入,所以调整语法):

   private void But_rnd_Click(object sender, EventArgs e)
    {
        LoadPlayers();
        List<string> results=new List<string>();
        Grouping(ref results);
        ShowResults(ref results);
    }

    private void Grouping(ref List<string> results)
    {
        Random r=new Random();
        while (Players.count() > 1)
        {
           int p1 = r.next(players.count());
           String p1s = players.get(p1);
           players.removeat(p1);
           int p2 = r.next(players.count());
           String p2s = players.get(p2);
           players.removeat(p2);

           results.add(//whatever string you use to combine p1 + p2);
        }
        if (players.count() > 0)
        {
            // add remaining players
        }
    }

答案 3 :(得分:0)

如果我理解正确,看起来你正试图生成一对玩家。我真的不明白你在循环中选择随机元素或退出条件的方法。

您遇到的一个问题是,当您在分组函数中运行循环时,您将删除元素,从而更改players.count()的值以及元素的位置

即。如果阵列中有5个玩家,则表示他们是“A”,“B”,“C”,“D”,“E”。 在循环的第一次迭代中,i = 0,count = 5。索引0处的元素是“A”,并且我们随机选择“D”作为对。我们删除这两个,列表变为“B”,“C”,“E”。 在下一次迭代中,i = 1,索引1处的元素是“C”(不是“B”)

建议:尝试更简单的算法,例如以下(伪代码):

 while (list.count() > 1)
 {

        randomElementIndex = random(1, list.count())
        firstElem = list[0]; // the first one will always be different, since we remove the old first one
        secondElem = list[randomElementIndex]

        print output

        list.remove(firstElem)
        list.remove(secondElem)
 }

答案 4 :(得分:0)

非常感谢所有人!我纠正了我的代码错误。我的分组()方法:

    private void Grouping(ref List<string> results)
    {
        Random r=new Random();
        int index = 0;
        for (int i = players.Count /2; i > 0;i--)
        {
            index++;
            back:
            int rand1 = r.Next(players.Count);
            int rand2 = r.Next(players.Count);
            if (rand1 == rand2)
            {
                // Random numbers are same :(
                goto back; // I know - it'll be slowly :(
            }
            results.Add(index + ".: " + players[rand1] + " - " + players[rand2]);
            players.Remove(players[rand1]);
            players.Remove(players[rand2]);
        }
    }

答案 5 :(得分:0)

我不确定你的意图,但看起来你正试图拿一个球员名单,并创建两支随机选择相同数量球员的球队。

其他人已经回复了为什么你的代码不起作用。我只是想建议一种不同的方式来做我认为你想做的事情。鉴于你的球员名单:

//create a new list of players mixing your original list in random order 
var shuffled = players.OrderBy(item => rnd.Next()).ToList();

int n = shuffled.Count() / 2; // there will be n elements per team
bool isOdd=(shuffled.Count() % 2 == 1); // is the number of players odd?

// the first team will have the first n players in the "shuffled" list
var firstTeam = shuffled.Take(n).ToList();
// the second team will have the next n players in the "shuffled" list
var secondTeam= shuffled.Skip(n).Take(n).ToList();

// if the players were in odd number take the last one
// who wasn't chosen in any team
// (that was me in school, that's why I'm a developer now!! :) )    
string didNotPlay=(isOdd)? shuffled.Last() : null;