16

スタック オーバーフローは初めてなので、フォーマットがサイトに合わない場合は申し訳ありません。最近プログラミングを始めたばかりで、2週間ほど経ちました。私はhttp://openbookproject.net/thinkcs/python/english3e/index.htmlから python を学ん でいますが、今まではすべてがうまくいっていて、何時間も立ち往生していました。私はたくさんグーグルで検索しましたが、私の問題に対する適切な解決策を見つけることができなかったので、ここにいます.

CH17 で説明されているように、問題なく OldMaidGame() を実行しようとしています。http://openbookproject.net/thinkcs/python/english3e/ch17.html - ほとんどのコードも前の章からのものです。

私が見つけたのは、Deck.remove、Hand.remove_matches、またはその他の種類の削除機能を機能させることができないということです。いくつかのデバッグの後、プログラムが特定のカードがデッキ/ハンド/その他に存在するかどうかをチェックするときに問題が発生することがわかりました。それは決して一致することはできません。その後、チャプター(ch16)を振り返ってみると、「デッキ/ハンド/etcの場合:remove(カード)」などが. オブジェクトのcmp () を呼び出して、カードが実際にデッキ/手札などに存在するかどうかを判断します。これは、電子書籍の指定されたコードに 'ace's を追加した後の私のバージョンの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自体はうまくいっているようですが、それを改善する方法についていくつかのヒントを使用できます(エースチェックなど)。したがって、デッキ/ハンド チェックでカードが常に false を返す理由がわかりません。これは与えられた削除機能でした:

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 not in list) が表示されました。別の私たちかそこらで、私もそれを機能させることができたかもしれませんが、元の「デッキ/手札/その他のカード」などを修正できないため、間違った道を進んでいるような気がするので、私はいくつかの答え/ヒントを探してここに来ました。

読んでいただきありがとうございます。

--------------------- 編集 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
>>> 

@DSM が正常に使用した card.py も試してみましたが、そこでもエラーが発生します。たとえば、並べ替え関数で 2 つのカード オブジェクトを比較できないと表示されます。
だから私は疑問に思っていました.Python 3.2に問題があるのでしょうか、それとも構文がどこかで変更されたのでしょうか?

4

5 に答える 5

6

「それで、Python 3.2 の問題なのか、それとも構文がどこかで変更されたのではないかと考えていました。」

Python 3.2 を実行しているのですか? これは Python 3 では機能しません: Python 3 は__cmp__!を使用しません。

データ モデルを参照してください( を探します__eq__)また、Python 3 の新機能も読んで、見落としがちなその他の事柄を確認してください。

申し訳ありませんが、これは私たち Python プログラマーの責任です。これをもっと早くキャッ​​チする必要がありました。ほとんどの人はおそらくすべてのコードを見て、そのソースが明らかに python 2 コードであることに何も考えずに気付き、それが私たちが取り組んでいるものだと思い込んでいました。cmp 関数は Python 3.2 にも存在しませんが、 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__、そのうちの 1 つを実装して 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 関数は、次のようにもう少し簡潔に(そしてIMHOより簡単に)書くことができます:

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 から始めて、そこに投稿した remove 関数を追加すると、うまくいくようです:

>>> 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、私の ace-over-king 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

他の人が指摘したように、比較機能は機能するはずです。そこで何が起こっていたのかを理解するには、より詳細な情報が必要です。提案について:

  1. ランクの最後にエースを置き、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 に答える
0

デッキ変数に問題があるようです。remove 関数が空白のデッキを持つ別のオブジェクトを指しているか、名前空間に問題があります。remove 関数はデッキ オブジェクトの一部ですか?

いくつかのプリント デッキ ラインを追加することをお勧めします。1 つは初期化された直後で、内部にあるものを確認するためのもので、もう 1 つは remove を呼び出したときのものです。

于 2011-06-02T15:46:26.787 に答える