Ruby can do that . . .

Peter Harkins at Push.cx wrote a bit of code in python. I've noticed Python and Ruby being very similar, so I wanted to rewrite his example code in Ruby and see how it compares. His motivation was to basically find a way to compare two objects for equality (containing the same values for its members) since the == operator doesn't function as expected.

Here's my code:

values = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
suits = ['h', 'c', 'd', 's']

class Card

attr_reader :value, :suit
protected :value, :suit

def initialize(value, suit)
@value, @suit = value, suit

def to_str
sprintf "<card: %s, %s>", @value, @suit

def equal( other )
(@value == other.value) && (@suit == other.suit)


This may not be the best implementation, I'm new to Ruby so there is probably a better way to do it! I created attribute readers and then set them as protected so only other instances of class "Card" can call it (don't need public access right now, so why open that door?). Constructor takes the two parameters and sets the class variables. The method to_str . . . It was my impression that if I defined a to_str method and did "puts card" it would automagically call the to_str object.. but no, it didn't work. Ok then what is the advantage over defining to_s?

When I run it

card1 = Card.new("3", "s")
puts card1.to_str
# prints
<card: 3,s>

card2 = Card.new("3", "s")
puts card2.to_str
# prints
<card: 3,s>
puts "is card 1 the same as card 2?"
puts card1 == card2

# result would be:
# is card 1 the same as card 2?
# false

puts "now using equal method"
puts card1.equal(card2)

# result would be:
# now using equal method
# true

BTW - printing the instance var itself renders something goofy like:

It's the object id, which is why you can't use the == operator, since each id is different


Blogger Adz said...

You can actually override the "==" operator:

def ==(other)
(@value == other.value) && (@suit == other.suit)

Although maybe you'd like to leave == for object identity comparison....

As to the "to_str" method... well, you'd use "to_s" which is what puts calls.

Why? Well because puts is supposed to print objects of any type. to_str would only exist in objects which are 'would-be-strings'.

It's a bit confusing, but these links may help:

"to_str .... is reserved for classes that are made to be used in place of
Strings, when they need to be converted to true Strings"

"With to_str, Matz is giving us a simpler way of demonstrating that our classes can be used as strings directly. Nearly all builtin methods use to_str to determine if an object is (or can be used as) a String. Think of it: if you extend the String class, then you have to write alternate methods (sub!, length, append, etc.) tailored to your needs.
So why not use to_s? Because to_s coerces objects into strings. So, to_str is an implicit cast, whereas to_s is an explicit cast.

Think of timestamps. We want to be able to easily convert a timestamp into a String for printing:

puts "Time.now: " + Time.now.to_s
#=> Time.now: Mon Aug 04 13:37:43 MDT 2003

But we don’t want to load a File based on a timestamp:

File.open( Time.now )
#=> TypeError: cannot convert Time into String
from (irb):3:in `initialize'
from (irb):3:in `open'
from (irb):3

Generally, we just don’t need a timestamp to act as a String. So we use to_s to explicitly convert it."

8:21 AM  

Post a Comment

Links to this post:

Create a Link

<< Home