【问题标题】:Divide a List of objects into sub-lists without creating additional objects将对象列表划分为子列表而不创建其他对象
【发布时间】:2018-09-28 08:15:12
【问题描述】:

我正在尝试在中断后摆脱编码锈迹,因此承担了创建简单纸牌游戏的任务。我知道我犯了一个菜鸟错误,但我似乎找不到解决方案。

我正在尝试实现一些概念性的 shuffle 方法,其中一种涉及将牌组清空到多个单独的堆栈中,然后再次重新编译成牌组。

我的deckCards 的标准List<T>。我的目标是从'temp_deck' 中取出一张顶牌[0] 并将其添加到堆/堆x,从而将其从temp_deck 的顶部移除。然后将最上面的牌添加到堆/堆叠y,然后是z,直到temp_deck 没有更多牌。然后我可以继续操作 3 堆并将它们添加回最后的甲板。

我的问题是,由于某种原因,它会将temp_deck 中的每个card 添加到每一堆中,因此每堆x, y, z 有52 张牌。我该如何解决这个问题,以便在这些堆之间划分我的列表而不是相乘?

这是我当前(第 n 次)划分列表的尝试:

    public void Shuffle_b(Deck deck)
    {
        Deck temp_deck = deck;
        Deck x = new Deck();
        x.Cards.Clear();
        Deck y = x;
        Deck z = y;

        while (temp_deck.Cards.Count !=0)
        {

            if (temp_deck.Cards.Count != 0)
            {
                x.Cards.Add(temp_deck.Cards[0]);
                temp_deck.Cards.Remove(temp_deck.Cards[0]);

                if (temp_deck.Cards.Count != 0)
                {
                    y.Cards.Add(temp_deck.Cards[0]);
                    temp_deck.Cards.Remove(temp_deck.Cards[0]);

                    if (temp_deck.Cards.Count != 0)
                    {
                        z.Cards.Add(temp_deck.Cards[0]);
                        temp_deck.Cards.Remove(temp_deck.Cards[0]);
                    }
                }
            }
        }
   }

【问题讨论】:

  • 无关,您的顶部if 是不必要的。您已经使用 while 循环对此进行了测试。一致性很好,但如果你想这样做,可以使用MoveCard 函数,这将具有删除嵌套的额外好处。此外,您的函数中的唯一(有效)非临时变量是temp_deck。您应该在函数中重新考虑该名称。
  • 您在代码中声明了 4 个新的卡组,但 xyz 都指向同一个内存位置,temp_deck 指向同一个内存位置为deck,因此您实际上总共只有 2 个卡组,它们之间有 5 个引用。
  • 感谢各位的详细解释!这让我很头疼。

标签: c#


【解决方案1】:

您的问题是您将甲板 y 和 z 设置为指向您曾经创建的同一个对象 x。换行:

    Deck y = x;
    Deck z = y;

    Deck y = new Deck();
    Deck z = new Deck();

你应该明白 x, y, z 是对一个对象的引用,当你分配 y = x 时,你将 y 指向 x 指向的同一个东西。

【讨论】:

  • 顺便说一句,我的评论是关于 temp_deck 指向此函数处理的唯一本地永久集合。
【解决方案2】:

当您将一个对象分配给另一个对象时,您并不是在创建另一个对象的副本,而是在说“让我的新对象指向与此现有对象相同的位置”。所以在你的代码中你已经声明了 4 个新的卡组,但是 xyz 都指向同一个内存位置,而 temp_deck 指向同一个内存位置 deck,所以你真的只有 2 个套牌,它们之间有 5 个参考。

相反,如果您想要一个全新的对象,则必须使用 new 关键字(或调用一些返回新对象的方法)。

这里有一个例子可以帮助说明这一点。我们从创建 3 个新卡片组开始,然后循环遍历卡片组中传递给该方法的每个项目。在循环内部,我们创建了一个新的牌组,它指向我们之前创建的三个之一的内存位置(由循环内的“i”索引值选择)。

基本上我们取索引(i 的值),加一(因为它是从零开始的),然后除以 3 后得到余数。如果余数为 1,则使用甲板 x,如果余数为2,使用deck y,否则(余数为0)使用deck z

在每次迭代结束时,addTo 甲板消失了——它只是循环内的一个临时指针。最后,我们的三个甲板上同样装满了从甲板上传入的物品:

public static void Shuffle_b(Deck deck)
{
    if (deck == null || !deck.Cards.Any()) return;

    Deck x = new Deck();
    Deck y = new Deck();
    Deck z = new Deck();

    for (int i = 0; i < deck.Cards.Count; i++)
    {
        // Add one to i, then get the remainder from dividing it by 3
        int deckNo = (i + 1) % 3;

        // If the remainder is 1, use deck x; if it's 2 use deck y; else use deck z
        Deck addTo = (deckNo == 1) ? x : (deckNo == 2) ? y : z;

        // Add the card to our selected deck
        addTo.Cards.Add(deck.Cards[i]);
    }

    // Now x contains the cards from 'deck' at indexes: 0, 3, 6, 9, etc...
    // And y contains the cards from 'deck' at indexes: 1, 4, 7, 10, etc...
    // And z contains the cards from 'deck' at indexes: 2, 5, 8, 11, etc..
}

【讨论】:

  • 这是解决问题的绝佳方法。我在 WIP 中有类似的东西,但为了分析我的错误而进行了简化。
【解决方案3】:

你关注的是yield关键字

if (match condition)
 yield return element

以您在正确的 if 中输入的方式构造调用将产生您需要的结果,而无需创建更多对象。

【讨论】:

  • 我不完全确定 OP 为什么要创建多个套牌来洗牌,或者他是否正在洗牌。我同样不确定这将如何帮助他。显然,在没有进一步更改的情况下将收益放入他现有的代码中是不会的。你能详细说明一下吗?
  • 这里没有答案。只是对 OP 没有采用的迭代器方法的模糊指针
  • @zzxyz 因为你不明白。只需使用 yield 来避免创建新对象。
猜你喜欢
  • 2011-09-07
  • 2023-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-12
  • 1970-01-01
相关资源
最近更新 更多