WeakRef and Object#hash

I’m trying to implement a weak key hash to use for generic objects.
I’m more than happy to use Object#hash and Object#eql? as they come
with the distribution, but when I make the WeakRef, the hash values
don’t come out the same way:

irb(main):002:0> a = Object.new
=> #Object:0x402c9b44
irb(main):003:0> a.hash
=> 538332578
irb(main):004:0> a = WeakRef.new(a)
=> #Object:0x402c9b44
irb(main):005:0> a.hash
=> 538323898

Is this by design, or is this a bug somewhere?

– Samuel

Is there an assignment version of Hash#values_at, so I can assign
multiple keys at once? Something like this:

hash.set([:a, :b, :c], [1,2,3])

such that I end up with hash[:a] = 1, hash[:b] = 2, and hash[:c] = 3.

Easy enough to write, but it’d be silly to do so if it’s already there.

-Mark

“Samuel Tesla” samuel@alieniloquent.com schrieb im Newsbeitrag
news:87zne8q76l.fsf@hiro.int.procacious.com

I’m trying to implement a weak key hash to use for generic objects.
I’m more than happy to use Object#hash and Object#eql? as they come
with the distribution, but when I make the WeakRef, the hash values
don’t come out the same way:

irb(main):002:0> a = Object.new
=> #Object:0x402c9b44
irb(main):003:0> a.hash
=> 538332578
irb(main):004:0> a = WeakRef.new(a)
=> #Object:0x402c9b44
irb(main):005:0> a.hash
=> 538323898

Is this by design, or is this a bug somewhere?

I guess it’s by design. But you can fix this easily by sub classing
WeakRef

class WeakHashRef < WeakRef
def initialize(*args)
super
end

def hash
begin
getobj.hash
rescue WeakRef::RefError
0
end
end

def eql?(ref)
begin
getobj.id == ref.getobj.id
rescue WeakRef::RefError
false
end
end
end

Then you need to sub class Hash and override some methods if you want the
WeakHash to look like a normal hash from the outside.

Regards

robert

hash = { :a => 3, :b => 2, :c => 1, :d => 0}
hash.update({ :a => 1, :b => 2, :c => 3 })

-austin

···

On Fri, 5 Dec 2003 12:42:05 +0900, Mark J. Reed wrote:

Is there an assignment version of Hash#values_at, so I can assign
multiple keys at once? Something like this:

hash.set([:a, :b, :c], [1,2,3])

such that I end up with hash[:a] = 1, hash[:b] = 2, and hash[:c] = 3.

Easy enough to write, but it’d be silly to do so if it’s already there.

-Mark


austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2003.12.04
* 23.43.54

“Robert Klemme” bob.news@gmx.net writes:

I guess it’s by design. But you can fix this easily by sub classing
WeakRef

Indeed. One would think that would work, however. Look at the
following two code samples.

···

Sample 1 - test1.rb

require ‘weakref’

a = WeakRef.new(Object.new) # We’ll just never make a strong ref
GC.start
puts a

Output:

test1.rb:8:in `puts’: Illegal Reference - probably recycled

(WeakRef::RefError) from test1.rb:8

END test1.rb

Sample 2 - test2.rb

require ‘weakref’

class WeakRef; end

a = WeakRef.new(Object.new) # We’ll just never make a strong ref
GC.start
puts a

Output:

Object:0x402911a0

END test2.rb

As you can see, I’m not even adding methods, and it causes the weak
reference to not be recycled by the garbage collector. Somehow, I
don’t think this is intended behavior. I don’t know enough about Ruby
internals to say for sure (although I’m reading the code right now, so
I will soon enough).

Matz?

Cheerio,
Samuel
(Trying to figure this out, sure is fun)

“Robert Klemme” bob.news@gmx.net writes:

Then you need to sub class Hash and override some methods if you want the
WeakHash to look like a normal hash from the outside.

Indeed, with the exception of the problem I posted about the class no
longer being a a weak reference, here’s that subclass. Works out
fairly well:

test.rb

require ‘weakref’

class WeakKey < WeakRef
def initialize(aRef)
super(aRef)

# We need to change #eql? on the incoming object because the
# method defined in object.c does not actualy check
# #object_id it actually compares the VALUE structs passed
# in.  So when I override #object_id below, it doesn't completely
# take care of the problem.

class << aRef
  def eql?(ref)
    begin
      self.object_id == ref.object_id
    rescue WeakRef::RefError
      false
    end
  end
end

end

We need to override this so that anything that asks for our ID

will actually receive the ID of the object we are pretending to be.

def object_id
begin
getobj.object_id
rescue WeakRef::RefError
0
end
end

Object#id is deprecated, but people use it still. Let’s make sure

they get the proper ID when they do.

alias_method :id, :object_id

From Robert Klemme. This simply calls the hash function from the

actual object.

def hash
begin
getobj.hash
rescue WeakRef::RefError
0
end
end

Also from Robert Klemme. Same idea as #hash.

def eql?(ref)
begin
getobj.object_id == ref.object_id
rescue WeakRef::RefError
false
end
end
end

a = Object.new
b = WeakKey.new(a)
puts(a.eql?(b).to_s) # => true
puts(b.eql?(a).to_s) # => true

hash = { :a => 3, :b => 2, :c => 1, :d => 0}
hash.update({ :a => 1, :b => 2, :c => 3 })

I know that, but in this case I have the keys and values in two
arrays and want to match them up by position. In other words, I
want to do this:

keys.each_with_index do
    >k, i|
    hash[k] = values[i]
end

The equivalent of Perl @hash{@keys} = @values.

-Mark

···

On Fri, Dec 05, 2003 at 01:45:36PM +0900, Austin Ziegler wrote:

“Samuel Tesla” samuel@thoughtlocker.net schrieb im Newsbeitrag
news:878ylqa9py.fsf@hiro.int.procacious.com

“Robert Klemme” bob.news@gmx.net writes:

I guess it’s by design. But you can fix this easily by sub classing
WeakRef

Indeed. One would think that would work, however. Look at the
following two code samples.

Sample 1 - test1.rb

require ‘weakref’

a = WeakRef.new(Object.new) # We’ll just never make a strong ref
GC.start
puts a

Output:

test1.rb:8:in `puts’: Illegal Reference - probably recycled

(WeakRef::RefError) from test1.rb:8

END test1.rb

Sample 2 - test2.rb

require ‘weakref’

class WeakRef; end

a = WeakRef.new(Object.new) # We’ll just never make a strong ref
GC.start
puts a

Output:

Object:0x402911a0

END test2.rb

As you can see, I’m not even adding methods, and it causes the weak
reference to not be recycled by the garbage collector.

I had similar experiences about references not beeing cleared. Maybe
someone with more insight (Matz?) should comment on this. Maybe only an
instance’s type is checked against WeakRef and not if it’s a subtype of
that.

Regards

robert

Unfortunately, that won’t work in Ruby because arrays can be keys, whereas
in Perl, hash keys can only be scalars.

What you can do is:

hash = nil; keys.zip(value) { |k, v| (hash ||= {})[k] = v }

-austin

···

On Fri, 5 Dec 2003 13:57:04 +0900, Mark J. Reed wrote:

On Fri, Dec 05, 2003 at 01:45:36PM +0900, Austin Ziegler wrote:

hash = { :a => 3, :b => 2, :c => 1, :d => 0}
hash.update({ :a => 1, :b => 2, :c => 3 })
I know that, but in this case I have the keys and values in two arrays
and want to match them up by position. In other words, I want to do this:

keys.each_with_index do

k, i|
hash[k] = values[i]
end

The equivalent of Perl @hash{@keys} = @values.


austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2003.12.05
* 08.59.33

Hi,

···

At Fri, 5 Dec 2003 13:57:04 +0900, Mark J. Reed wrote:

The equivalent of Perl @hash{@keys} = @values.

Once I’d proposed similar;

hash[*keys] = *values

but cannot remember how it was concluded.


Nobu Nakada

Hi,

···

In message “Re: WeakRef and Object#hash” on 03/12/08, “Robert Klemme” bob.news@gmx.net writes:

As you can see, I’m not even adding methods, and it causes the weak
reference to not be recycled by the garbage collector.

I had similar experiences about references not beeing cleared. Maybe
someone with more insight (Matz?) should comment on this. Maybe only an
instance’s type is checked against WeakRef and not if it’s a subtype of
that.

The weakly referenced object is also considered as referenced from C
stack region. It’s due to Ruby’s conservative nature of GC.

						matz.

Now that is handy. I assume there’s a historical reason for the
name ‘zip’? Because I would never have thought it meant
“iterate in parallel with”. :slight_smile:

-Mark

···

On Fri, Dec 05, 2003 at 11:03:07PM +0900, Austin Ziegler wrote:

What you can do is:

hash = nil; keys.zip(value) { |k, v| (hash ||= {})[k] = v }

Hi –

[Changed the subject to something more informative :slight_smile: ]

hash = { :a => 3, :b => 2, :c => 1, :d => 0}
hash.update({ :a => 1, :b => 2, :c => 3 })
I know that, but in this case I have the keys and values in two arrays
and want to match them up by position. In other words, I want to do this:

keys.each_with_index do

k, i|
hash[k] = values[i]
end

The equivalent of Perl @hash{@keys} = @values.

Unfortunately, that won’t work in Ruby because arrays can be keys, whereas
in Perl, hash keys can only be scalars.

What you can do is:

hash = nil; keys.zip(value) { |k, v| (hash ||= {})[k] = v }

Or, using the handy flattenx extension from RAA:

irb(main):001:0> require ‘flattenx’
=> true
irb(main):002:0> a = [ [1],2,3 ]; b = [ 4, [5,6], 7]
=> [4, [5, 6], 7]
irb(main):003:0> Hash[*a.zip(b).flatten_by(1)]
=> {[1]=>4, 2=>[5, 6], 3=>7}

(to avoid over-flattening)

David

···

On Fri, 5 Dec 2003, Austin Ziegler wrote:

On Fri, 5 Dec 2003 13:57:04 +0900, Mark J. Reed wrote:

On Fri, Dec 05, 2003 at 01:45:36PM +0900, Austin Ziegler wrote:


David A. Black
dblack@wobblini.net

That would be handy, but I don’t think it’s possible to define such
a method in current Ruby. The parameters to = are flattened;
you wouldn’t be able to tell where the keys stopped and the values
started, unless you assumed that keys.length == values.length, which
is not always a valid assumption where I’ve seen the Perl idiom used.

-Mark

···

On Fri, Dec 05, 2003 at 11:32:51PM +0900, nobu.nokada@softhome.net wrote:

Once I’d proposed similar;

hash[*keys] = *values

“Yukihiro Matsumoto” matz@ruby-lang.org schrieb im Newsbeitrag
news:1070890833.524202.5797.nullmailer@picachu.netlab.jp…

Hi,

As you can see, I’m not even adding methods, and it causes the weak
reference to not be recycled by the garbage collector.

I had similar experiences about references not beeing cleared. Maybe
someone with more insight (Matz?) should comment on this. Maybe only
an
instance’s type is checked against WeakRef and not if it’s a subtype of
that.

The weakly referenced object is also considered as referenced from C
stack region. It’s due to Ruby’s conservative nature of GC.

Hm, not sure what exactly you mean. I was assuming, the line

wr = WeakRef.new Object.new

leads to exactly one ref (the weak ref to be precise) to the instance.

Anyway, can I circumvent this by doing:

def create; Object.new; end
wk = WeakRef.new create

?

Regards

robert
···

In message “Re: WeakRef and Object#hash” > on 03/12/08, “Robert Klemme” bob.news@gmx.net writes:

Hi –

···

On Fri, 5 Dec 2003, Mark J. Reed wrote:

On Fri, Dec 05, 2003 at 11:03:07PM +0900, Austin Ziegler wrote:

What you can do is:

hash = nil; keys.zip(value) { |k, v| (hash ||= {})[k] = v }

Now that is handy. I assume there’s a historical reason for the
name ‘zip’? Because I would never have thought it meant
“iterate in parallel with”. :slight_smile:

It’s more that the result is ‘zipped’, as the teeth of a zipper become
interleaved when the zipper is zipped (one from the right, one from the
left, one from the right…).

David


David A. Black
dblack@wobblini.net

The reason it's called "zip" is that it's stolen directly from
Python, which has a function by that name which does just that.

Whar do you think that zip do in Haskell ?

Guy Decoux

Hm, not sure what exactly you mean.

matz is trying to say that it's hard to know if a variable will be gc'ed,
because internally ruby can have reference to the object that you are not
aware.

                                   I was assuming, the line
wr = WeakRef.new Object.new
leads to exactly one ref (the weak ref to be precise) to the instance.

yes, it has only one ref. Now write it (like in the original example)

   class WeakRef; end
   wr = WeakRef.new Object.new

and if you are lucky the variable will not be gc'ed because internally
ruby has a reference to it.

Guy Decoux

“ts” decoux@moulon.inra.fr schrieb im Newsbeitrag
news:200312081429.hB8ETig01761@moulon.inra.fr

Hm, not sure what exactly you mean.

matz is trying to say that it’s hard to know if a variable will be
gc’ed,
because internally ruby can have reference to the object that you are
not
aware.

Aha. But “WeakRef.new Object.new” is not supposed to generate any side
effects that create additional references to the newly created instance of
class Object, is it? At least not, if WeakRef#initialize is unmodified.

                               I was assuming, the line

wr = WeakRef.new Object.new
leads to exactly one ref (the weak ref to be precise) to the
instance.

yes, it has only one ref. Now write it (like in the original example)

class WeakRef; end
wr = WeakRef.new Object.new

and if you are lucky the variable will not be gc’ed because internally
ruby has a reference to it.

To prevent misunderstandings: I was talking about the Object instance, not
“wr”. Currently I can’t see how opening class WeakRef and closing it
without doing anything else affects the references to the newly created
Object instance. Could you please clarify this? Thanks a lot!

Regards

robert

To prevent misunderstandings: I was talking about the Object instance, not
"wr". Currently I can't see how opening class WeakRef and closing it
without doing anything else affects the references to the newly created
Object instance. Could you please clarify this? Thanks a lot!

Object.new create a new reference : in reality this reference is one of
the reference internally created by ruby when it has called

    class WeakRef; end

When the GC run it will mark indirectly this reference, because it still
have the struct which make reference to this object.

Guy Decoux