From: Christoph Date: 2004-06-19T04:28:21+09:00 Subject: Re: How to make generic #== method? Zakaria wrote: .... > > URL? I know I'm lazy :) Also checkout [ruby-talk :48114] (+ google) and http://www.rubygarden.org/ruby?CompareByValue. Personally I would tend to implement such a class as a subclass of a Struct class ... class LoopTag < Struct.new(:param,:contents) def initialize(param) super(param,[]) end ... end Just for the heck of it here is a cleaned up version of the same problem I wrote two years ago, which is sort (of) recursion and thread safe. There is an obvious similarity to the idea of Joel content method returning a Hash. --- module CompareByValue def ==(other) if not instance_of?(other.class) false elsif equal?(other) true elsif id < other.id CompareByValue.comp?(self,other) else CompareByValue.comp?(other,self) end end end class << CompareByValue class Seen < Hash Pair = Struct.new(:l,:r) def initialize(l,r) store(Pair.new(l.id,r.id),true) end def seen?(l,r) self[Pair.new(l.id,r.id)] end def default(key) store(key,true) nil end def remove(l,r) delete Pair.new(l.id,r.id) end end # symbol mangling SEEN_COMPS = "CBV#{id}_comps".intern NUMS_CALLS = "CBV#{id}_calls".intern def comp?(l,r) return true if thread_local_seen?(l,r) begin Thread.current[NUMS_CALLS] +=1 return Comp.new.comp?(l,r) rescue Thread.current[SEEN_COMPS].remove(l,r) raise ensure if (Thread.current[NUMS_CALLS] -= 1).zero? Thread.current[SEEN_COMPS].clear end end end def thread_local_seen?(l,r) unless comps = Thread.current[SEEN_COMPS] Thread.current[NUMS_CALLS] = 0 Thread.current[SEEN_COMPS] = Seen.new(l,r) false else comps.seen?(l,r) end end class Comp < Hash def comp?(l,r) return true if l.equal?(r) vars = l.instance_variables.sort! return false unless vars == r.instance_variables.sort! store(l.id,r.id) vars.each do |name| ll = l.instance_eval(name) rr = r.instance_eval(name) if CompareByValue === ll return false unless ll.instance_of?(rr.class) if rr_seen_id = self[ll.id] return false unless rr_seen_id == rr.id else return false unless comp?(ll,rr) end else return false unless ll == rr end end return true end end end --- /Christoph