Sets of Objects: Composition and Card Representation, Exams of Electrical and Electronics Engineering

The concept of sets of objects and composition in programming. It uses the example of card objects to explain how to represent and compare objects using integers as codes for suits and ranks. The document also covers the creation and manipulation of deck objects, which contain lists of cards.

Typology: Exams

Pre 2010

Uploaded on 09/17/2009

koofers-user-1in
koofers-user-1in 🇺🇸

8 documents

1 / 9

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Chapter 15
Sets of objects
15.1 Composition
By now, you have seen several examples of composition. One of the first exam-
ples was using a method invocation as part of an expression. Another example
is the nested structure of statements; you can put an if statement within a
while loop, within another if statement, and so on.
Having seen this pattern, and having learned about lists and objects, you should
not be surprised to learn that you can create lists of objects. You can also create
objects that contain lists (as attributes); you can create lists that contain lists;
you can create objects that contain objects; and so on.
In this chapter and the next, we will look at some examples of these combina-
tions, using Card objectsasanexample.
15.2 Card objects
If you are not familiar with common playing cards, now would be a good time to
get a deck, or else this chapter might not make much sense. There are fifty-two
cards in a deck, each of which belongs to one of four suits and one of thirteen
ranks. The suits are Spades, Hearts, Diamonds, and Clubs (in descending order
in bridge). The ranks are Ace, 2, 3, 4, 5, 6, 7, 8, 9, 10, Jack, Queen, and King.
Depending on the game that you are playing, the rank of Ace may be higher
than King or lower than 2.
If we want to define a new object to represent a playing card, it is obvious
what the attributes should be: rank and suit. It is not as obvious what type
pf3
pf4
pf5
pf8
pf9

Partial preview of the text

Download Sets of Objects: Composition and Card Representation and more Exams Electrical and Electronics Engineering in PDF only on Docsity!

Chapter 15

Sets of objects

15.1 Composition

By now, you have seen several examples of composition. One of the first exam- ples was using a method invocation as part of an expression. Another example is the nested structure of statements; you can put an if statement within a while loop, within another if statement, and so on.

Having seen this pattern, and having learned about lists and objects, you should not be surprised to learn that you can create lists of objects. You can also create objects that contain lists (as attributes); you can create lists that contain lists; you can create objects that contain objects; and so on.

In this chapter and the next, we will look at some examples of these combina- tions, using Card objects as an example.

15.2 Card objects

If you are not familiar with common playing cards, now would be a good time to get a deck, or else this chapter might not make much sense. There are fifty-two cards in a deck, each of which belongs to one of four suits and one of thirteen ranks. The suits are Spades, Hearts, Diamonds, and Clubs (in descending order in bridge). The ranks are Ace, 2, 3, 4, 5, 6, 7, 8, 9, 10, Jack, Queen, and King. Depending on the game that you are playing, the rank of Ace may be higher than King or lower than 2.

If we want to define a new object to represent a playing card, it is obvious what the attributes should be: rank and suit. It is not as obvious what type

158 Sets of objects

the attributes should be. One possibility is to use strings containing words like "Spade" for suits and "Queen" for ranks. One problem with this implementation is that it would not be easy to compare cards to see which had a higher rank or suit.

An alternative is to use integers to encode the ranks and suits. By “encode,” we do not mean what some people think, which is to encrypt or translate into a secret code. What a computer scientist means by “encode” is “to define a mapping between a sequence of numbers and the items I want to represent.” For example:

Spades → 3 Hearts → 2 Diamonds → 1 Clubs → 0

An obvious feature of this mapping is that the suits map to integers in order, so we can compare suits by comparing integers. The mapping for ranks is fairly obvious; each of the numerical ranks maps to the corresponding integer, and for face cards:

Jack → 11 Queen → 12 King → 13

The reason we are using mathematical notation for these mappings is that they are not part of the Python program. They are part of the program design, but they never appear explicitly in the code. The class definition for the Card type looks like this:

class Card: def init(self, suit=0, rank=0): self.suit = suit self.rank = rank

As usual, we provide an initialization method that takes an optional parameter for each attribute.

To create an object that represents the 3 of Clubs, use this command:

threeOfClubs = Card(0, 3)

The first argument, 0 , represents the suit Clubs.

160 Sets of objects

The disadvantage is that if we modify a class attribute, it affects every instance of the class. For example, if we decide that “Jack of Diamonds” should really be called “Jack of Swirly Whales,” we could do this:

>>> card1.suitList[1] = "Swirly Whales" >>> print card Jack of Swirly Whales

The problem is that all of the Diamonds just became Swirly Whales:

>>> print card 3 of Swirly Whales

It is usually not a good idea to modify class attributes.

15.4 Comparing cards

For primitive types, there are conditional operators (<, >, ==, etc.) that compare values and determine when one is greater than, less than, or equal to another. For user-defined types, we can override the behavior of the built-in operators by providing a method named cmp. By convention, cmp takes two parame- ters, self and other, and returns 1 if the first object is greater, -1 if the second object is greater, and 0 if they are equal to each other.

Some types are completely ordered, which means that you can compare any two elements and tell which is bigger. For example, the integers and the floating- point numbers are completely ordered. Some sets are unordered, which means that there is no meaningful way to say that one element is bigger than another. For example, the fruits are unordered, which is why you cannot compare apples and oranges.

The set of playing cards is partially ordered, which means that sometimes you can compare cards and sometimes not. For example, you know that the 3 of Clubs is higher than the 2 of Clubs, and the 3 of Diamonds is higher than the 3 of Clubs. But which is better, the 3 of Clubs or the 2 of Diamonds? One has a higher rank, but the other has a higher suit.

In order to make cards comparable, you have to decide which is more important, rank or suit. To be honest, the choice is arbitrary. For the sake of choosing, we will say that suit is more important, because a new deck of cards comes sorted with all the Clubs together, followed by all the Diamonds, and so on.

With that decided, we can write cmp :

15.5 Decks 161

def cmp(self, other):

check the suits

if self.suit > other.suit: return 1 if self.suit < other.suit: return -

suits are the same... check ranks

if self.rank > other.rank: return 1 if self.rank < other.rank: return -

ranks are the same... it’s a tie

return 0

In this ordering, Aces appear lower than Deuces (2s).

As an exercise, modify cmp so that Aces are ranked higher than Kings.

15.5 Decks

Now that we have objects to represent Cards, the next logical step is to define a class to represent a Deck. Of course, a deck is made up of cards, so each Deck object will contain a list of cards as an attribute.

The following is a class definition for the Deck class. The initialization method creates the attribute cards and generates the standard set of fifty-two cards:

class Deck: def init(self): self.cards = [] for suit in range(4): for rank in range(1, 14): self.cards.append(Card(suit, rank))

The easiest way to populate the deck is with a nested loop. The outer loop enumerates the suits from 0 to 3. The inner loop enumerates the ranks from 1 to

  1. Since the outer loop iterates four times, and the inner loop iterates thirteen times, the total number of times the body is executed is fifty-two (thirteen times four). Each iteration creates a new instance of Card with the current suit and rank, and appends that card to the cards list.

The append method works on lists but not, of course, tuples.

15.6 Printing the deck

As usual, when we define a new type of object we want a method that prints the contents of an object. To print a Deck, we traverse the list and print each Card:

15.7 Shuffling the deck 163

>>> deck = Deck() >>> print deck 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

And so on. Even though the result appears on 52 lines, it is one long string that contains newlines.

15.7 Shuffling the deck

If a deck is perfectly shuffled, then any card is equally likely to appear anywhere in the deck, and any location in the deck is equally likely to contain any card.

To shuffle the deck, we will use the randrange function from the random module. With two integer arguments, a and b, randrange chooses a random integer in the range a <= x < b. Since the upper bound is strictly less than b, we can use the length of a list as the second parameter, and we are guaranteed to get a legal index. For example, this expression chooses the index of a random card in a deck:

random.randrange(0, len(self.cards))

An easy way to shuffle the deck is by traversing the cards and swapping each card with a randomly chosen one. It is possible that the card will be swapped with itself, but that is fine. In fact, if we precluded that possibility, the order of the cards would be less than entirely random:

164 Sets of objects

class Deck: ... def shuffle(self): import random nCards = len(self.cards) for i in range(nCards): j = random.randrange(i, nCards) self.cards[i], self.cards[j] = self.cards[j], self.cards[i]

Rather than assume that there are fifty-two cards in the deck, we get the actual length of the list and store it in nCards.

For each card in the deck, we choose a random card from among the cards that haven’t been shuffled yet. Then we swap the current card (i) with the selected card (j). To swap the cards we use a tuple assignment, as in Section 9.2:

self.cards[i], self.cards[j] = self.cards[j], self.cards[i]

As an exercise, rewrite this line of code without using a sequence assignment.

15.8 Removing and dealing cards

Another method that would be useful for the Deck class is removeCard, which takes a card as a parameter, removes it, and returns true (1) if the card was in the deck and false (0) otherwise:

class Deck: ... def removeCard(self, card): if card in self.cards: self.cards.remove(card) return 1 else: return 0

The in operator returns true if the first operand is in the second, which must be a list or a tuple. If the first operand is an object, Python uses the object’s cmp method to determine equality with items in the list. Since the cmp in the Card class checks for deep equality, the removeCard method checks for deep equality.

To deal cards, we want to remove and return the top card. The list method pop provides a convenient way to do that: