16

这是我第一次遇到堆栈溢出,所以如果格式不适合该网站,我很抱歉。我最近才开始学习编程,从那时起已经过去了将近 2 周。我正在从http://openbookproject.net/thinkcs/python/english3e/index.html学习 python, 直到现在一切都很好,我只是被困了几个小时。我用谷歌搜索了很多,但找不到合适的解决方案来解决我的问题,所以我在这里。

我试图让 OldMaidGame() 运行没有问题,如 CH17 中所述。http://openbookproject.net/thinkcs/python/english3e/ch17.html - 大部分代码也来自上一章。

我发现我无法让 Deck.remove、Hand.remove_matches 或任何其他类型的移除功能工作。经过一些调试后,我发现当程序检查给定卡是否存在于甲板/手/等中时会出现问题。它永远无法匹配。然后在回顾了这一章之后,(在第 16 章中),我发现 'if card in deck/hand/etc: remove(card)' 等查找 . cmp () 来判断卡片是否真的存在于deck/hand/etc中。这是我在电子书的给定代码上添加 'ace' 后的cmp版本。

def __cmp__(self, other):
    """ Compares cards, returns 1 if greater, -1 if lesser, 0 if equal """
    # check the suits
    if self.suit > other.suit: return 1
    if self.suit < other.suit: return -1
    # suits are the same... check ranks
    # check for aces first.
    if self.rank == 1 and other.rank == 1: return 0
    if self.rank == 1 and other.rank != 1: return 1
    if self.rank != 1 and other.rank == 1: return -1
    # check for non-aces.
    if self.rank > other.rank: return 1
    if self.rank < other.rank: return -1
    # ranks are the same... it's a tie
    return 0

cmp本身似乎很好 afaik,我可以使用一些技巧来使其更好(例如使用 ace 检查)。所以我不知道为什么牌组/手牌检查中的牌总是返回假。这是给定的删除功能:

class Deck:
    ...
    def remove(self, card):
        if card in self.cards:
            self.cards.remove(card)
            return True
        else:
            return False

拼命想让它工作,我想出了这个:

class Deck:
    ...
    def remove(self, card):
        """ Removes the card from the deck, returns true if successful """
        for lol in self.cards:
            if lol.__cmp__(card) == 0:
                self.cards.remove(lol)
                return True
        return False

似乎工作正常,直到我转向其他非工作删除功能:

class OldMaidHand(Hand):
    def remove_matches(self):
        count = 0
        original_cards = self.cards[:]
        for card in original_cards:
            match = Card(3 - card.suit, card.rank)
            if match in self.cards:
                self.cards.remove(card)
                self.cards.remove(match)
                print("Hand {0}: {1} matches {2}".format(self.name, card, match))
                count = count + 1
        return count

我又做了一些调整:

class OldMaidHand(Hand):
    def remove_matches(self):
        count = 0
        original_cards = self.cards[:]
        for card in original_cards:
            match = Card(3 - card.suit, card.rank)
            for lol in self.cards:
                if lol.__cmp__(match) == 0:
                    self.cards.remove(card)
                    self.cards.remove(match)
                    print("Hand {0}: {1} matches {2}".format(self.name, card, match))
                    count = count + 1
        return count

卡的删除工作正常,但是当我尝试删除匹配时它会给出错误(x 不在列表中)。另一个我们的左右,我可能也能做到这一点,但是因为我已经感觉我走错了路,因为我无法修复原来的“卡牌/手牌/等”等,我来这里寻找一些答案/提示。

感谢您的阅读,非常感谢您提供的任何帮助:)

--------------------- 编辑 1 * >

这是我当前的代码: http: //pastebin.com/g77Y4Tjr

--------------------- 编辑 2 * >

我已经尝试了这里建议的每一个cmp,但我仍然无法找到带有“in”的卡。

>>> a = Card(0, 5)
>>> b = Card(0, 1)
>>> c = Card(3, 1)
>>> hand = Hand('Baris')
>>> hand.add(a)
>>> hand.add(b)
>>> hand.add(c)
>>> d = Card(3, 1)
>>> print(hand)
Hand Baris contains
5 of Clubs
 Ace of Clubs
  Ace of Spades
>>> d in hand.cards
False
>>> 

我也试过 card.py @DSM 已经成功使用,我也有错误,比如在 sort 函数中它说它不能比较两个卡片对象。
所以我想知道,也许是 Python 3.2 的问题,或者语法在某个地方发生了变化?

4

5 回答 5

6

“所以我想知道,也许是 Python 3.2 的问题,或者语法在某个地方发生了变化?”

哦,您正在运行 Python 3.2? 这在 Python 3 中永远不会起作用:python 3 不使用__cmp__!

查看数据模型(查找__eq__。另请阅读Python 3中的新增功能,了解其他一些很容易错过的内容。

抱歉,这是我们 Python 程序员的责任;我们应该早点发现这一点。大多数人可能查看了所有代码,甚至没有考虑就意识到源代码显然是 python 2 代码,并假设这就是我们正在使用的代码。在 Python 3.2 中甚至不存在 cmp 函数,但它不会因 NameError 而崩溃的原因是因为__cmp__从未调用过。

如果我在 Python 3.2 中运行代码,我会准确地重现您的问题:

>>> c = Card(0,2)
>>> str(c)
'2 of Clubs'
>>> c in [c]
True
>>> c in Deck().cards
False

在 Python 3 中,您要么实现所有丰富的 cmps,要么实现__eq__其中一个并使用 total_ordering 装饰器。

from functools import total_ordering

@total_ordering
class Card(object):
    """Represents a standard playing card."""
    suit_names = ["Clubs", "Diamonds", "Hearts", "Spades"]
    rank_names = [None, "Ace", "2", "3", "4", "5", "6", "7", 
              "8", "9", "10", "Jack", "Queen", "King"]
    def __init__(self, suit=0, rank=2):
        self.suit = suit
        self.rank = rank
    def __str__(self):
        return '%s of %s' % (Card.rank_names[self.rank],
                             Card.suit_names[self.suit])
    def __repr__(self): return str(self)
    def __lt__(self, other):
        t1 = self.suit, self.rank
        t2 = other.suit, other.rank
        return t1 < t2
    def __eq__(self, other):
        t1 = self.suit, self.rank
        t2 = other.suit, other.rank
        return t1 == t2


>>> c = Card(2,3)
>>> c
3 of Hearts
>>> c in Deck().cards
True
于 2011-06-03T00:43:44.967 回答
1

我也无法重现该错误。这对我来说可以。我唯一的建议是,您可能不应该在迭代列表时修改列表(即,在 self.cards 的循环中调用 self.cards.remove)。这无法解释为什么使用“in”的版本对你不起作用。

您的 cmp 函数可以写得更简洁一些(恕我直言更简单):

def __cmp__(self, other):
    """ Compares cards, returns 1 if greater, -1 if lesser, 0 if equal """
    return cmp((self.suit,  self.rank == 1,  self.rank),
              (other.suit, other.rank == 1, other.rank))

或者,如果您愿意:

    return (cmp(self.suit, other.suit) or
            cmp(self.rank == 1, other.rank == 1) or
            cmp(self.rank, other.rank))
于 2011-06-02T15:39:55.400 回答
0

我似乎无法重现无法通过 Deck.remove 移除卡片的问题。如果我从thinkpython 站点上的 card.py开始并添加您在此处发布的删除功能,它似乎可以工作:

>>> deck = Deck()
>>> str(deck).split('\n')
['Ace of Clubs', '2 of Clubs', '3 of Clubs', '4 of Clubs', '5 of Clubs', '6 of Clubs', '7 of Clubs', '8 of Clubs', '9 of Clubs', '10 of Clubs', 'Jack of Clubs', 'Queen of Clubs', 'King of Clubs', 'Ace of Diamonds', '2 of Diamonds', '3 of Diamonds', '4 of Diamonds', '5 of Diamonds', '6 of Diamonds', '7 of Diamonds', '8 of Diamonds', '9 of Diamonds', '10 of Diamonds', 'Jack of Diamonds', 'Queen of Diamonds', 'King of Diamonds', 'Ace of Hearts', '2 of Hearts', '3 of Hearts', '4 of Hearts', '5 of Hearts', '6 of Hearts', '7 of Hearts', '8 of Hearts', '9 of Hearts', '10 of Hearts', 'Jack of Hearts', 'Queen of Hearts', 'King of Hearts', 'Ace of Spades', '2 of Spades', '3 of Spades', '4 of Spades', '5 of Spades', '6 of Spades', '7 of Spades', '8 of Spades', '9 of Spades', '10 of Spades', 'Jack of Spades', 'Queen of Spades', 'King of Spades']
>>> len(deck.cards)
52
>>> c = Card(suit=0, rank=8)
>>> str(c)
'8 of Clubs'
>>> c in deck.cards
True
>>> deck.remove(c)
True
>>> len(deck.cards)
51
>>> c in deck.cards
False
>>> str(deck).split('\n')
['Ace of Clubs', '2 of Clubs', '3 of Clubs', '4 of Clubs', '5 of Clubs', '6 of Clubs', '7 of Clubs', '9 of Clubs', '10 of Clubs', 'Jack of Clubs', 'Queen of Clubs', 'King of Clubs', 'Ace of Diamonds', '2 of Diamonds', '3 of Diamonds', '4 of Diamonds', '5 of Diamonds', '6 of Diamonds', '7 of Diamonds', '8 of Diamonds', '9 of Diamonds', '10 of Diamonds', 'Jack of Diamonds', 'Queen of Diamonds', 'King of Diamonds', 'Ace of Hearts', '2 of Hearts', '3 of Hearts', '4 of Hearts', '5 of Hearts', '6 of Hearts', '7 of Hearts', '8 of Hearts', '9 of Hearts', '10 of Hearts', 'Jack of Hearts', 'Queen of Hearts', 'King of Hearts', 'Ace of Spades', '2 of Spades', '3 of Spades', '4 of Spades', '5 of Spades', '6 of Spades', '7 of Spades', '8 of Spades', '9 of Spades', '10 of Spades', 'Jack of Spades', 'Queen of Spades', 'King of Spades']

如果我也用你的替换它似乎__cmp__也可以:

>>> deck = Deck()
>>> c = Card(suit=0,rank=1)
>>> c in deck.cards
True
>>> deck.remove(c)
True
>>> len(deck.cards)
51

所以必须有所不同。你能把你的整个代码——以及一些演示错误的代码——转储到某个地方(pastebin、gist 等)吗?


(FWIW,我的王牌 cmp 看起来像这样:

def __cmp__(self, other):
    def aceshigh(r): return 14 if r==1 else r
    t1 = self.suit, aceshigh(self.rank)
    t2 = other.suit, aceshigh(other.rank)
    return cmp(t1, t2)

练习:删除幻数。)

于 2011-06-02T15:34:23.233 回答
0

听起来你的甲板变量有问题。要么删除函数指向带有空白卡片组的不同对象,要么您有命名空间问题。是甲板对象的删除功能的一部分吗?

我建议添加一些打印甲板线。一个在它初始化之后,看看它里面有什么,一个就像你调用remove一样。

于 2011-06-02T15:46:26.787 回答
0

正如其他人指出的那样,您的比较功能应该可以工作,需要更多细节来弄清楚那里发生了什么。至于建议:

  1. 将 Ace 放在行列末尾,使用 0-12 映射行列。这对我来说似乎是自然的方法。

  2. 利用标准库:

    A.random.shuffle用来洗牌。

    B.cmp用于处理比较。

    C.在我看来collections.defaultdict是一种更清洁的方法。remove_matches

  3. 建议的__str__方法真的很烦人。

  4. 实施__repr__

替代实现:

from collections import defaultdict
import random

class Card(object):
    suits = ["Clubs", "Diamonds", "Hearts", "Spades"]
    ranks = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King", "Ace"]

    def __init__(self, suit=0, rank=0):
        self.suit = suit
        self.rank = rank

    def __str__(self):
        return self.ranks[self.rank] + " of " + self.suits[self.suit]

    def __repr__(self):
        return self.__str__()

    def __cmp__(self, other):
        return cmp((self.suit, self.rank), (other.suit, other.rank))

class Deck(object):
    def __init__(self):
        self.cards = []
        for suit in range(4):
            for rank in range(13):
                self.cards.append(Card(suit=suit, rank=rank))

    def shuffle(self):
        random.shuffle(self.cards)

    def remove(self, card):
        if card in self.cards:
            self.cards.remove(card)

    def pop(self):
        return self.cards.pop()

    def is_empty(self):
        if len(self.cards) is 0:
            return True
        return False

    def deal(self, hands, num_cards=999):
        num_hands = len(hands)
        for i in range(num_cards):
            if self.is_empty(): break   # break if out of cards
            card = self.pop()           # take the top card
            hand = hands[i % num_hands] # whose turn is next?
            hand.add(card)              # add the card to the hand

class Hand(Deck):
    def __init__(self, name=""):
       self.cards = []
       self.name = name

    def add(self,card):
        self.cards.append(card)

class OldMaidHand(Hand):
    def remove_matches(self):
        matches = defaultdict(list)
        for card in self.cards:
            matches[card.rank].append(card)
        for cards in matches.values():
            if len(cards) == 2:
                print("Hand {0}: {1} matches {2}".format(self.name, *cards))
                for card in cards:
                    self.remove(card)
于 2011-06-02T16:08:23.163 回答