A newbie needs insight into some Ruby proboems...

  • Thread starter Just Another Victim of the Ambient Morality
  • Start date
J

Just Another Victim of the Ambient Morality

I have some problems that I'm sure must be routine for Ruby developers
but I'm a little stumped on how to solve them. Any help is greatly
appreciated!

My first problem is that I have a class and I want to define some
operators, like equality. It's a simple class and its operators are equally
simple. Equality is simple the equality of the member variables of my
class. So, I have code that looks something like this:

class Object
attr_accessor :first, :second, :third

def initialize (first, second, third)
@first, @second, @third = first, second, third
end

def == (right) # should this be Eql?
bool = true
[:first, :second, :third].each do |member|
bool = (bool and (method(member).call ==
right.method(member).call))
end
bool
end
end

What I'm trying to do is automate iterating over my member variables
performing a cumulative equality test. The best I was able to come up with
was a single list of members but there must be some way, through inspection,
to programmatically iterator over them to do this test. Also, it looks like
there might be something a little more efficient than calling "method" on
each member and then calling "call" on each method object returned. Is
there?

My second problem is that I would like to put this simple object in a
Set (as broken as it may be). How do I do this? I know this doesn't
magically just work. I have to define something like a "hash" method but I
don't know what hash is supposed to return, exactly, for things like Set and
Hash to work. Can someone explain this to me?

Thank you for your responses! I will use them to be a better Ruby
programmer...
 
D

Dumaiu

It's funny, but just last week I was proposing this pattern for
attribute generation to someone else. The general technique I've
worked out starts with something like,

class C
ATTRIBUTES = %w(first second third)

the master list of names. Then,

ATTRIBUTES.each { |att| attr_accessor(att) }

creates the actual accessors. I would construct the class's
constructor with

eval <<-EVAL
def initialize( #{ATTRIBUTES.join(', ')} )
@#{ATTRIBUTES.join(', @')} = #{ATTRIBUTES.join(', ')}
end
EVAL

which is pretty ugly but generates the same method as appears in your
example. You can manipulate the strings however you like; however,
notice that as written it needs at least one attribute. Finally,

def ==(rhs)
not ATTRIBUTES.detect { |att|
rhs.__send__(att) != self.__send__(att)
}
end
end# C

is the quickest search I can think of. Enumerable#detect()
short-circuits on the first hit--in this case, the first failed
comparison. I've simply inverted the truth values going in and the
truth value coming out. 'rhs' is a C++ abbrev. for 'right-hand side.'
I hope you find some of this useful, as you helped me by reminding me
of Ruby's parallel assignment, which I have a tendency to forget. If
what I have written still seems like too much work, the Struct class
and its buffed relative, OpenStruct, exist precisely for
metaprogramming accessors.
As far as a Set goes, stuff actually will magically just work as long
as you don't change the default behavior too much. Unless you're going
for that degree, you can pass on writing hash functions! The Set API
is quite straightforward and easily found in RDoc form.

My little contribution,
-Jonathan
 
D

Dumaiu

Tim Hunter wrote:
....
This also works. Remember that attr_acccessor is just a method and it
can take any number of arguments:

I had forgotten that, as well...
The Comparable module adds ==, <, >, and other comparison methods to Foo
by calling Foo's <=> method.

and that I didn't know. Thank you.

-Jonathan
 
J

Just Another Victim of the Ambient Morality

Tim Hunter said:
How about something like this:

class Foo
include Comparable
attr_accessor :a, :b, :c
def initialize(a, b, c)
@a, @b, @c = a, b, c
end
def <=>(other)
[@a, @b, @c] <=> [other.a, other.b, other.c]
end
end

The Comparable module adds ==, <, >, and other comparison methods to Foo
by calling Foo's <=> method.

Wow, that's really useful, both the Comparable mixin and the <=> method
for Arrays. I'm going to remember both, thank you...

The hash and eql? method in Object will be fine for your class. You don't
need to override them unless you have special needs.

I don't think this is true...


include 'set'

class Class
attr_accessor :member

def initialize
@member = "member"
end
end

foo = Class.new
bar = Class.new

foo == bar # this will evaluate to false

set = Set.new
set.add foo
set.include? foo # evaluates to true, as expected
set.include? bar # but this evaluates to false...


Now, maybe it's not surprising that "set.include? bar" evaluates to
false considering how "foo == bar" is false but it demonstrates that the
built-in eql? method doesn't work. If there's another way to think about
how the built-in eql? and hash methods work, I'd like to hear it because it
seems to me that any reasonable definition of those methods for Class would
allow foo and bar to equate...
Thank you...
 
J

Just Another Victim of the Ambient Morality

Dumaiu said:
It's funny, but just last week I was proposing this pattern for
attribute generation to someone else. The general technique I've
worked out starts with something like,

class C
ATTRIBUTES = %w(first second third)

the master list of names. Then,

ATTRIBUTES.each { |att| attr_accessor(att) }

creates the actual accessors. I would construct the class's
constructor with

Hey, I used this pattern when I used an array of method names for a
second time, in another method. This really shows the dynamic nature of
Ruby!

eval <<-EVAL
def initialize( #{ATTRIBUTES.join(', ')} )
@#{ATTRIBUTES.join(', @')} = #{ATTRIBUTES.join(', ')}
end
EVAL

I didn't realize that #{code} can be used anywhere. I had thought that
it was used in quotes to evaluate ruby code. It's making me reconsider its
use since it looks expensive. Is it, compared to string concatenation and
such?
Thank you...
 
K

Karl von Laudermann

Just said:
I didn't realize that #{code} can be used anywhere. I had thought that
it was used in quotes to evaluate ruby code.

AFAIK, #{code} can only be used within strings. In the code above,
everything in between <<-EVAL and EVAL is a "here document", which is a
form of multi-line string.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top