Problem with Array#delete?

Hi all,

In messing around with Array#delete I think I’ve uncovered a problem.
Consider this example:

class Foo
def ==(other)
$array.clear
false
end
end

arr = [1, 2, 3]
arr << Foo.new
Arr << ‘b’

$array = arr

arr.delete(‘a’)

p arr # => []
p $array # => []

That’s what I would expect. It did the comparison, and called the
Foo#== method, which in turn cleared the receiver.

But if you do this instead:

arr.delete(1)

You end up with:

[nil, nil, #Foo:0x2865ff4]
[nil, nil, #Foo:0x2865ff4]

What’s happening?

Thanks,

Dan

On Tue, Jun 24, 2008 at 4:43 PM, Daniel B. [email protected]
wrote:

end
p $array # => []
[nil, nil, #Foo:0x2865ff4]
[nil, nil, #Foo:0x2865ff4]

What’s happening?

Well, I haven’t dug into the implementation of Array#delete enough to
fully
explain the results but.

You do know that after the assignment

$array = arr

You’ve made the global variable $array reference the same object to
which
you are sending delete.

So the $array.clear in the Foo#== method is clearing that object which
is
the receiver of the delete method.

Now in general ruby iterator methods don’t expect the enumerated object
to
change during the iteration and that’s just what’s happening.

This is no doubt confusing the delete method.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On Tue, Jun 24, 2008 at 5:53 PM, Rick DeNatale [email protected]
wrote:

Well, I haven’t dug into the implementation of Array#delete enough to fully
explain the results but.

And having now looked at the Ruby 1.8 implementation of Array#delete, I
think I know exactly what’s going on.

Here’s a pseudo-ruby translation of the C code:

class Array
def delete(obj)
i1 = i2 = 0
while i1 < self.length
e = self[i1]
unless e == obj
self[i2] = e
i2 += 1
end
i1 += 1
end
if i2 = self.length # We didn’t find any matches
return yield(obj) if block_given?
else
self.length = i2
end
end

def length=(l)
    # A method to set the length of the array and make any necessary

internal adjustments
end
end

So, you start out with arr = $array = [1, 2, 3, aFoo, ‘b’]

And then call
arr.delete(1)

Now lets’s step through the delete method

i1 i2 self e e == 1
Action
0 0 [1, 2, 3, aFoo, ‘b’] 1 true so we
just
increment i1 giving
1 0 [1, 2, 3, aFoo, ‘b’] 2 false so we
change self[i0] and increment i2 giving
2 1 [2, 2, 3, aFoo, ‘b’] 3 false so we
change
self[i0] and increment i2 giving
3 2 [2, 3, 3, aFoo, ‘b’] Foo BANG! this clears
$array which is also self which is also arr, since all reference the
same
object. The state is now:
3 2 [nil, nil, nil, nil] Foo false
Foo#==
returns false so we set self[i2] = e and increment i2 giving:
4 3 [nil, nil, aFoo, nil]

And the loop ends and self.length = 3 adjusts things so that self == arr

$array == [nil, nil, aFoo]


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/