[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Problem with Array#delete

Matthew B Gardner

8/18/2007 5:00:00 AM

Hello, I'm baffled by the following problem:

I have an Account class and an external array that keeps them in a list. I'm
trying to delete the account objects one at a time as needed, by:

class Account
def quit
array_name.delete self
end
end

However, this is deleting every account in the list (array_name == []). I've
verified that the account objects are unique. Just for an example, this is
the equivalent of what's happening:

class Account
def quit arr
arr.delete self
end
end

a1 = Account.new
a2 = Account.new
arr = []
arr << a1
arr << a2
a1.quit arr
arr #=> []


Has anyone else experienced this problem, or know what the likely cause is? I
have other arrays that work in the exact same fashion and work as expected.

Thank you for any help,
Matt

15 Answers

Morton Goldberg

8/18/2007 6:17:00 AM

0

On Aug 18, 2007, at 12:59 AM, Matthew B Gardner wrote:

> Hello, I'm baffled by the following problem:
>
> I have an Account class and an external array that keeps them in a
> list. I'm
> trying to delete the account objects one at a time as needed, by:
>
> class Account
> def quit
> array_name.delete self
> end
> end
>
> However, this is deleting every account in the list (array_name ==
> []). I've
> verified that the account objects are unique. Just for an example,
> this is
> the equivalent of what's happening:
>
> class Account
> def quit arr
> arr.delete self
> end
> end
>
> a1 = Account.new
> a2 = Account.new
> arr = []
> arr << a1
> arr << a2
> a1.quit arr
> arr #=> []
>
> Has anyone else experienced this problem, or know what the likely
> cause is? I
> have other arrays that work in the exact same fashion and work as
> expected.

I ran your code and I didn't have a problem: it did what you
expected. Are you sure that the code you posted is exactly the same
as the code that is giving you trouble?

Regards, Morton

Dan Zwell

8/18/2007 6:19:00 AM

0

Matthew B Gardner wrote:
> Hello, I'm baffled by the following problem:
>
> I have an Account class and an external array that keeps them in a list. I'm
> trying to delete the account objects one at a time as needed, by:
>
> class Account
> def quit
> array_name.delete self
> end
> end
>
> However, this is deleting every account in the list (array_name == []). I've
> verified that the account objects are unique. Just for an example, this is
> the equivalent of what's happening:
>
> class Account
> def quit arr
> arr.delete self
> end
> end
>
> a1 = Account.new
> a2 = Account.new
> arr = []
> arr << a1
> arr << a2
> a1.quit arr
> arr #=> []
>
>
> Has anyone else experienced this problem, or know what the likely cause is? I
> have other arrays that work in the exact same fashion and work as expected.
>
> Thank you for any help,
> Matt
>
>

Wow, that was a well-asked question. This seems like a Ruby bug, though
I can't reproduce it. When I run the code you posted, arr[] still
contains a2 (as it should). I am using Ruby 1.8.6 (on Linux). What are
you using?

Dan

Matthew B Gardner

8/18/2007 6:35:00 AM

0

Sorry, I only meant the code I posted as an example of what is happening --
the actual code would be a little hard to gather and format, but I will do so
if the following isn't enough information:

ruby -v #=> ruby 1.8.4 (2005-12-24) [i486-linux]

Ok, I threw this code in to gather some info, and following it is the
printout:

p world.characters.class
p world.characters.size
world.characters.each do |ch|
p ch.class
p ch.object_id
end
world.characters.delete(self)
p world.characters

Array
2
Character
-607336504
Character
-607410834
[]

Do I need to format up the code, or is there something telling from the
information here?

Thanks,
Matt

On Saturday 18 August 2007 02:17, Morton Goldberg wrote:
> On Aug 18, 2007, at 12:59 AM, Matthew B Gardner wrote:
> > Hello, I'm baffled by the following problem:
> >
> > I have an Account class and an external array that keeps them in a
> > list. I'm
> > trying to delete the account objects one at a time as needed, by:
> >
> > class Account
> > def quit
> > array_name.delete self
> > end
> > end
> >
> > However, this is deleting every account in the list (array_name ==
> > []). I've
> > verified that the account objects are unique. Just for an example,
> > this is
> > the equivalent of what's happening:
> >
> > class Account
> > def quit arr
> > arr.delete self
> > end
> > end
> >
> > a1 = Account.new
> > a2 = Account.new
> > arr = []
> > arr << a1
> > arr << a2
> > a1.quit arr
> > arr #=> []
> >
> > Has anyone else experienced this problem, or know what the likely
> > cause is? I
> > have other arrays that work in the exact same fashion and work as
> > expected.
>
> I ran your code and I didn't have a problem: it did what you
> expected. Are you sure that the code you posted is exactly the same
> as the code that is giving you trouble?
>
> Regards, Morton

Robert Klemme

8/18/2007 9:06:00 AM

0

On 18.08.2007 08:34, Matthew B Gardner wrote:
> Sorry, I only meant the code I posted as an example of what is happening --
> the actual code would be a little hard to gather and format, but I will do so
> if the following isn't enough information:
>
> ruby -v #=> ruby 1.8.4 (2005-12-24) [i486-linux]
>
> Ok, I threw this code in to gather some info, and following it is the
> printout:
>
> p world.characters.class
> p world.characters.size
> world.characters.each do |ch|
> p ch.class
> p ch.object_id
> end
> world.characters.delete(self)
> p world.characters
>
> Array
> 2
> Character
> -607336504
> Character
> -607410834
> []
>
> Do I need to format up the code, or is there something telling from the
> information here?


The crucial bit is missing: how did you define ==, eql? and hash in your
class? Did you define them? If not, using Struct might help because
that gives you those methods for free:

YourClass = Struct.new :name, :other_field do
def method_you_need
end
end

Then eql?, == and hash will be implemented in terms of "name" and
"other_field".

Kind regards

robert

Matthew B Gardner

8/18/2007 9:45:00 AM

0

I went a little different route, but that was indeed the problem. I just
defined == in my class, which I guess was comparing the class of the object
instead of the object_id. I did:

def == obj
self.equal?(obj)
end

If you have time, or if someone else does, is there a quick explanation of why
this happened in this instance, but not with similar classes that function
fine with essentially duplicate methods of adding to and deleting from
arrays? Hopefully I can avoid future problems if I understand what's
happening (I'm a hobby programmer, so I lack some technical expertise).

Thank you, and the other repliers, for the help -- it's very much appreciated.
-Matt

On Saturday 18 August 2007 05:09, Robert Klemme wrote:
> On 18.08.2007 08:34, Matthew B Gardner wrote:
> > Sorry, I only meant the code I posted as an example of what is happening
> > -- the actual code would be a little hard to gather and format, but I
> > will do so if the following isn't enough information:
> >
> > ruby -v #=> ruby 1.8.4 (2005-12-24) [i486-linux]
> >
> > Ok, I threw this code in to gather some info, and following it is the
> > printout:
> >
> > p world.characters.class
> > p world.characters.size
> > world.characters.each do |ch|
> > p ch.class
> > p ch.object_id
> > end
> > world.characters.delete(self)
> > p world.characters
> >
> > Array
> > 2
> > Character
> > -607336504
> > Character
> > -607410834
> > []
> >
> > Do I need to format up the code, or is there something telling from the
> > information here?
>
> The crucial bit is missing: how did you define ==, eql? and hash in your
> class? Did you define them? If not, using Struct might help because
> that gives you those methods for free:
>
> YourClass = Struct.new :name, :other_field do
> def method_you_need
> end
> end
>
> Then eql?, == and hash will be implemented in terms of "name" and
> "other_field".
>
> Kind regards
>
> robert

Robert Klemme

8/18/2007 12:14:00 PM

0

On 18.08.2007 11:44, Matthew B Gardner wrote:
> I went a little different route, but that was indeed the problem. I just
> defined == in my class, which I guess was comparing the class of the object
> instead of the object_id. I did:
>
> def == obj
> self.equal?(obj)
> end
>
> If you have time, or if someone else does, is there a quick explanation of why
> this happened in this instance,

If you just defined == the way you presented above then the issue should
not have occurred because equal? tests for identity. It's difficult to
comment on fragments when the whole code is not presented.

> but not with similar classes that function
> fine with essentially duplicate methods of adding to and deleting from
> arrays?

I am not sure what you mean by this. Can you elaborate?

> Hopefully I can avoid future problems if I understand what's
> happening (I'm a hobby programmer, so I lack some technical expertise).

There are some concepts you should keep in mind that have some
similarities and will typically wreck havoc on your code when confused.
Both define relations (in the mathematical sense) on objects.

First, there is "identity". Two objects are identical when they are
actually just one, i.e. the same instance.

Then, there is equivalence. Equivalence is defined per class. For
example two strings containing the same sequence of characters are
equivalent. Sometimes only identical instances are equivalent.

Now, these different concepts are implemented in Ruby via different methods:
eql? and == implement equivalence
equal? implements identity

Most containers (an Array is a container) use equivalence, namely
implemented via eql? to test whether some objects match (e.g. for
deletion), because it is the more flexible and more useful concept.
(Think of an Array of Strings and you want to delete one of them with a
certain character sequence, you would want to provide a string with that
sequence as template and not the exact same object in the array - which
you might not know beforehand.)

Now, there is a slight twist: since for some algorithms it's not
efficient to compare something against all elements in the container
(for example, Array#uniq would have to compare every element of the
array with every other element which is O(n*n), i.e. if you double the
elements in the Array you quadruple the number of comparisons
necessary). In those cases (unfortunately they are not all documented)
typically a Hash is used behind the scenes. For objects to work
properly as Hash key methods eql? *and* hash need to be implemented
properly.

Consequence is, that you should always implement eql? and hash (and also
== for consistency) reasons *if* you plan to use instances of your class
in these circumstances *and* want to define equivalence different than
via identity (which happens to be the default implementation in class
Object). Typically you will choose some fields for this and you must
also make sure that equivalent instances yield the same (!) hash code.
Normally you do that by applying some math operation (binary XOR is
frequently used, because it's fast and guarantees that all values used
influence the result) on the hash values of those members that you
determine as key elements for equivalence.

The easiest way to do that is by using Struct, because that will
generate a class with all the necessary methods. Example:

# name and age are key for Foo
Foo = Struct.new :name, :age do
attr_accessor :unimportant_other_attribute
end


irb(main):007:0> f1 = Foo.new("a", 10)
=> #<struct Foo name="a", age=10>
irb(main):008:0> f2 = Foo.new("a", 10)
=> #<struct Foo name="a", age=10>
irb(main):009:0> f1.hash
=> -2186440
irb(main):010:0> f2.hash
=> -2186440
irb(main):011:0> f1.eql? f2
=> true
irb(main):012:0> f1 == f2
=> true
irb(main):013:0> f1.equal? f2
=> false
irb(main):014:0> f1.unimportant_other_attribute = "bar"
=> "bar"
irb(main):015:0> f1.eql? f2
=> true
irb(main):016:0> f1.name = "hello"
=> "hello"
irb(main):017:0> f1.eql? f2
=> false

There is another thing you should be aware: numbers in Ruby actually
implement *two* different equivalence relations:

irb(main):018:0> 1 == 1.0
=> true
irb(main):019:0> 1.eql? 1.0
=> false
irb(main):020:0> 1.hash
=> 3
irb(main):021:0> 1.0.hash
=> 233071

But most classes treat == and eql? synonym.

Next week we'll dive into ordering and operator <=>. :-))

Kind regards

robert

Matthew B Gardner

8/18/2007 3:37:00 PM

0

> I went a little different route, but that was indeed the problem. I just
> defined == in my class, which I guess was comparing the class of the object
> instead of the object_id. I did:
>
> def == obj
>       self.equal?(obj)
> end

Ok, so it looks like this didn't work so well after all. Initially, I thought
it did because I reloaded the character.rb file while the program was running
and it worked fine. However, when I shutdown the program and started it back
up, I experienced the same problem (though it again worked after reloading
the file). I'll post all the necessary code:

def world
returns the world object
end

class World
def initialize
characters = []
end
end

class Account
def foo
@character = Character.new()
world.characters << @character
end
end

class Character
def quit
p self.object_id
p world.characters.class
p world.characters.size
world.characters.each do |ch|
p ch.class
p ch.object_id
end
world.characters.delete(self)
p world.characters
end
end

This is the output from Character#quit, with two character objects in the
world#characters array:

-607170236
Array
2
Character
-607170236
Character
-607240286
[]

As you can see, both Character objects are being deleted when only the first
should be (at least as far as my intention). In an attempt to fix this, I
added the following method:

class Character
def == obj
self.equal?(obj)
end
end

I thought this did fix it, because I reloaded the file and tested it -- and it
only deleted the targeted object. However, when I restarted the program, it
didn't work and deleted both objects again. Without changing anything, and
just reloading the file while the program was still running, it worked as
intended again and deleted only the targeted object.

I have an Account class that utilizes World#accounts (an Array object) in the
exact same way that Character utilizes World#characters, and the deletion of
accounts works fine.

Robert, thank you for your reply -- I hope that this answers the noted
questions that you had, and maybe even provides some more insight into what
the problem might be. Is there an explanation for why the Character#== method
works after being reloaded, but not at start time? Also, Robert, I reviewed
the portion of your reply regarding a Struct object, and it doesn't look like
it would be beneficial in my case -- I'm just trying to add and delete single
instances of a class from an array -- but I could be wrong?

Thanks again for any help,
Matt

David A. Black

8/18/2007 6:36:00 PM

0

Matthew B Gardner

8/18/2007 8:45:00 PM

0

Thank you to everyone who has tried to help me with this today -- I know for
certain -what- the problem is now, after adding in some testing. My Character
class, for whatever reason, is using == to evaluate class. So, when I am
trying to use Array#delete (array.delete(self) for a Character object), it's
deleting every Character object in that array instead of just that single
instance. I've tried to fix this issue by defining the == method in my
Character class like this:

def Character
def == obj
self.equal?(obj)
end
end

However, this doesn't initially work -- I have to reload the file during
runtime for it to utilize the above method. For sake of clarity, this is what
I mean:

Start program...
a = Character.new
b = Character.new
a == b #=> true
Reload character.rb file while program is running...
a == b #=> false

Hopefully that gets my meaning across. I'm not sure why Character#== isn't
initially getting used, or if there's something wrong with my declaration.
I'm also not sure why Character#== (prior to my defining Character#==) is
comparing class and my other classes aren't.

I hope that this description is satisfactory -- and thank you for any help
that can be offered.
-Matt

On Saturday 18 August 2007 14:35, David A. Black wrote:

David A. Black

8/18/2007 9:56:00 PM

0