将卡片添加到不可变卡片组的底部

Adding Cards to the bottom of an immutable Deck

背景: 我创建了一个自定义 Card 结构,其中 SuitValue 属性由枚举表示以模拟扑克牌。我创建了一个 Deck class,其中包含卡片并有多个操作选项。它将卡片存储为 Queue<Card> 因为这最合乎逻辑并且使大多数实现变得容易。

为了练习编码,我决定构建一个无法修改的 ImmutableDeck。它的方法 return 新 ImmutableDecks 并使用 out 参数来 return 绘制 Cards。我以 Eric Lippert 的 ImmutableStack 为模型,创建了一个 Empty Deck,将构造函数设为私有,并有两个成员:"Top Card" 和下一个 [=16] 的 "pointer" =].

public class ImmutableDeck
{
    static readonly EmptyDeck Empty = new EmptyDeck ();
    readonly Card top;
    readonly ImmutableDeck next;

    ImmutableDeck ()
    {
    }

    ImmutableDeck (Card top, ImmutableDeck next)
    {
        this.top = top;
        this.next = next;
    }

    public int Count => 1 + next.Count;
    public bool IsEmpty => this == Empty;
    public Card Top => top;

    public Card Bottom ()
    {
        var temp = next;
        while (!temp.next.IsEmpty)
        {
            temp = temp.next;
        }
        return temp.Top;
    }

    public ImmutableDeck DrawCard (out Card c)
    {
        c = top;
        return next;
    }

    public ImmutableDeck PutCardOnTop (Card c)
    {
        return new ImmutableDeck (c, this);
    }

    public ImmutableDeck PutCardOnBottom (Card c)
    {
        throw new NotImplementedException ("PutCardOnBottom");
    }



    public class EmptyDeck : ImmutableDeck
    {
        public new int Count
        {
            get;
        } = 0;

        public new bool IsEmpty
        {
            get;
        } = true;

        public new Card Top
        {
            get
            {
                throw new Deck.DeckEmptyException ();
            }
        }

        public new Card Bottom ()
        {
            throw new Deck.DeckEmptyException ();
        }

        public new ImmutableDeck DrawCard (out Card c)
        {
            throw new Deck.DeckEmptyException ();
        }

        public new ImmutableDeck PutCardOnTop (Card c)
        {
            return new ImmutableDeck (c, this);
        }

        public new ImmutableDeck PutCardOnBottom (Card c)
        {
            return new ImmutableDeck (c, this);
        }
    }
}

问题: 显然,这些牌组可以通过在空牌组之上的纸牌之上构建纸牌来构建。它相当强大。但是我正在努力实现一种允许将 Card 添加到底部的方法。到目前为止,我所想到的只是我需要在链中跟随 next 指针,直到到达终点(空),但我不确定接下来的步骤是什么。

一个好的解决方案将提供正确的工作代码 returns 一个新的 ImmutableDeck 实例,其中传入的 Card 现在是牌组中的最后一张牌以及对解决方案如何工作。

正如 Leandro 所说,有多种方法可以做到这一点。这是我的(使用您已有的 class)。我能看到的唯一问题是它要求您为 next 字段分配一个值,该字段在您的实现中是只读的。我明白为什么会这样(你不能在取出那张卡片之前修改另一张卡片下的内容),但在这种情况下,我确信不改变这个字段就可以做到这一点。

Public ImmutableDeck PutCardOnBottom (Card c)
{
    if (!this.next.IsEmpty)
        this.next.PutCardOnBottom(c);
    else
        this.next = new ImmutableDeck(c, ImmutableDeck.Empty);

    return this;
}

注意 :我不是 100% 确定 return 第一次调用的实例,但我认为自甲板顶部以来这是有意义的没变。

其他注意事项:我能看到的唯一问题(除了我之前提到的)是,如果你有 really big deck,你会遇到一个WhosebugException。我本可以用与您实施 Bottom 方法类似的方式完成它,但我认为 52 张卡片组不会成为问题,而且我喜欢递归函数:)

首先,如果您对可以廉价访问两端的不可变队列式数据结构感兴趣,请阅读我关于不可变双端队列的文章。

https://blogs.msdn.microsoft.com/ericlippert/2008/01/22/immutability-in-c-part-10-a-double-ended-queue/

双端队列通常比堆栈和队列复杂得多,但却是非常有趣的数据结构。

您问题的更简单解决方案是:将其分解为三个较小的问题:

  • 插入顶部 -- 您已经解决了这个问题。
  • 从顶部删除 -- 您已经解决了这个问题。
  • 反转一副牌。

如何实现逆向?好吧,如果一副牌是空的,那么它已经被颠倒了。如果它不是空的,则取出最上面的牌,将其插入空的牌组中,并继续这样做直到它是空的。

所以现在你可以反向操作了。插到下面是反的,插到上面,再反。

当然,从底部移除也同样简单:反转,从顶部移除,再反转。

使用一个列表,该列表会自动将卡片放在牌组底部。我有类似的东西,我使用了 List,这对我来说很完美。希望这可以帮助。