I think I finally figured out a solution to making a set/list
of "WeakRef"s.  The code is below.  It mimics much of the Set
interface.  This is an order of magnitude faster than (and less
memory) using WeakRef to implement this.

The main thing I was missing was to check to see if the object
was finalized after a successful _id2ref.  If it was finalized
that means I got an object that reclaimed the space my old
object had.

I wasn't sure that you could delete elements of a hash while
iterating over it, but it looks to work fine.  Should this be
OK?  Anybody see any race conditions or other problems with
this?


#!/bin/env ruby

class WeakRefList
    include Enumerable
    def finalizer(id);@ids.delete(id >> 1);end
    private :finalizer
    def initialize(enum=[],&block)
        replace(enum.collect(&block))
    end
    def add(o)
        @ids[o.__id__ >> 1] = true
        ObjectSpace.define_finalizer(o,method(:finalizer))
        self
    end
    alias << add
    def each(&block)
        @ids.each_key { |id|
            begin
                o = ObjectSpace._id2ref(id << 1)
                # double-check in case it was finalized
                block.call(o) if @ids.include?(id)
            rescue RangeError
            end
        }
        nil
    end
    def clear;@ids = {};self;end
    def merge(enum);enum.each{|o|add(o)};self;end
    def replace(enum);clear;merge(enum);self;end
    def delete(o);@ids.delete(o.__id__ >> 1);self;end
    def delete?(o);@ids.delete(o.__id__ >> 1)&&self;end
    def empty?;each{return(false)};true;end
    def include?(o);@ids.include?(o.__id__ >> 1);end
    alias member? include?
    def inspect;"#<#{self.class}: #{to_a.inspect}>";end
    def subtract(enum);enum.each{|o|delete(o)};self;end
    def size;n=0;each{n+=1};n;end
    alias length size
end


if __FILE__==$0
    require 'benchmark'
    class MyString < String; end
    weakrefs = WeakRefList.new
    $stdout.sync=true
    times = Benchmark.measure {
    10000.times { |i|
        print(".")
        obj = MyString.new("X"*rand(i+1))
        weakrefs << obj
        weakrefs.each { |o|
            MyString==o.class or
            raise("not a MyString: #{o.object_id}
#{o.inspect}")
        }
        weakrefs.include?(obj) or
            raise("#{obj.inspect} disappeared")
        if rand(10).zero?
            weakrefs.delete(obj)
            !weakrefs.include?(obj) or
                raise("#{obj.inspect} didn't delete")
        end
    }
    }
    p weakrefs
    p weakrefs.size
    p weakrefs.empty?
    p weakrefs.clear
    p weakrefs.size
    p weakrefs.empty?
    puts(times)
end




		
__________________________________ 
Do you Yahoo!? 
Make Yahoo! your home page 
http://www.yahoo.com/r/hs