> From: ts [mailto:decoux / moulon.inra.fr]
[...]

>  I'll try to explain what do ruby actually
> 
> M> def f(x); [x,x]; end
> M> def g(x); f f f f f f f f x; end
> M> (g g g 1) == (g g g 1) #=> true
> 
>  In this case it do something like this :
>    
>    class Array
>       def ==(other)
>          each_index do |i|
>             return false if not self[i] == other[i]
>          end
>          true
>       end
>    end

to be exact 

class Array
	def ==(other)
		return false unless other.kind_of? Array
		return false unless length == other.length
		# unfortunately the id-sanity check 
		# return true if  id == other.id
		# is missing - Why?	    	
          	each_index do |i|
             	return false if not self[i] == other[i]
          	end
          	true
       end
end

> 
>  This explain why it has problem with recursive array.
> 
> >> 
> >> def f(x); [x,x]; end
> >> def g(x); f f f f f f f f x; end
> >> p (g g g 1)
> 
>  In this case inspect is protected, and ruby do (this is *very* simplified) 
> 
>    class Array
>       def inspect
>          return '[]' if empty?
>          return '[...]' if @inspect.include? id
>          begin
>             @inspect.push id
>             str = '['
>             each do |i|
>                str << i.inspect
>             end
> 	    str << ']'
>          ensure
>             @inspect.pop
>          end
>          str
>       end
>    end
> 
>  where @inspect is in reality a thread local variable.
>
>  This is why it take a very long time to find 'p (g g g 1)' because all
> recursive calls are replaced with a 'begin ... ensure'

interesting ...  

> C> ##########################
> C> p ([1,3,3,[3,4],"a"] == [1,3,3,[3,4],"a"]) 
> C> p ([1,3,3,[3,4],"a"] == [1,3,3,[3,4],"b"])
> 
> C> ##########################
> C> a =[1,2,3]; a << a; b =[1,2]; b << b;  a  << a; b << a; 
> C> p a - b; p a; p b
> 
>  In this case it give the good result

good, but you told me that 

> pigeon% ruby -e 'a=[1]; a << a; b = [1] ; b << b; p b - a '
> [[1, [...]]]
> pigeon%

did this change? I get [] - that is  ''a == b '' - 
but this decision is debatable (the ladder interpretation
means that you identify array's with a special kind of
``marked graphs''). Anyway I thought about this problem
a little longer ...

Since ruby's equality algorithm  (for non-recursive array's- 
the  ``id-sanity check'' should be added so) never terminates
for RCO's, recursive container Objects (or whatever you want
to call them) it seems to me that you are basically free to
choose between ``a == b''  and ``a != b''. (The ladder
scheme basically boils down to infinite loop protection - 
see below.)

(Besides their entertaining curiosity value for me;-)  RCO's 
seem like very bad  ``type citizens'' - in many computer
languages you cannot form them and in math they are strictly
forbidden since they let to inconsistencies.

On the practical side the infinite recursion problem is
a consequence that you are performing an unsupported 
operation (that includes asking for equality! - if the 
objects are unequal you are still fine) and throwing an
exception actually seems like the right thing to-do - a  
''catchable'' ``SystemStackError'' exception is as good as
any other exception IMO.
Ideally the formation of an RCO would be flagged with an
exception at creation time but such a scheme would bring
the language to a crawl (that's a problem with liberal
languages like ruby that they cannot be practically
defended against all abuses at runtime).  

In conclusion I believe that the right thing to do is to
remove all infinite recursion protections against
RCO's - if you want to shoot your self in the foot you 
are free to-do so ... that would mean removing the 
infinite recursion protection from  #flatten(!) (the 
algorithm rejects some non recursive arrays anyway) - 
and guard  critical sections by trying  to catch 
``SystemStackError'' exceptions if you suspect that
a RCO might occur.
Unfortunately the taint/freeze scheme does not protected
against RCO's so adding some RCO detection facilities
might be a good idea? Maybe add an equality (flatten,
hash) for RCO's  passed on an untangle scheme as a
Module? (but I doubt that there will be many takers.)

  
> C> ##########################
> C> def f(x); [x,x]; end
> C> def g(x); f f f f f f f f x; end
> C> def h(x); g g g g g g g g x; end
> C> def i(x);  h h h h h h h h x; end
> C> def j(x); i i i i  i i i   x;end
> 
> C> p (j(1) == j(1))
> 
>  Here it take too long time to compute it.

it would take 2**(8*8*8*7 +1) - 1 = 2**3585 - 1 Object
comparisons (not counting the length comparisions and 
type check) - note who ever that the memory usage in 
contrast to a true RCO won't go through the roof -  

Christoph

Ps.  Could you send me your patch? - maybe I could
take a look at it?. Anyway try ``your ruby'' on a 
couple of nasty potentially equal recursive arrays.


----------------------------
$ ruby UntangleVar.rb
true
false
[[1, [...]]]
[3]
[1, 2, 3, [...], [...]]
[1, 2, [...], [1, 2, 3, [...], [...]]]
[3]
[1, 2, 3, [...], [...]]
[1, 2, [...], [1, 2, 3, [...], [...]]]
[[1, 2, 3, [...], [...]]]
[[1, 2, 3, [...], [...]]]
true
----------------------------
class Object
def  no_loop (id_bag) ; true end		
end


class IdBag
def initialize
	@bag = {}	
end
def initialize
	@bag = {}
end
def  include_pair a, b
	@bag[a+ (b << Fixnum::Maxlog)] = 1
end	
def included_pair? a,b
	@bag.key?  (a+ (b << Fixnum::Maxlog))
end
private
class Fixnum
Maxlog =  32  
end	                    
end                        

	
class Array
def  no_loop (id_bag)
		level_id_bag = IdBag.new
		each do  |r|
			next if level_id_bag.included_pair? id, r.id
			level_id_bag.include_pair id, r.id
			return false unless r.detect_loop (id_bag)
			return false if id_bag.included_pair? id, r.id
			id_bag.include_pair id,r.id
		end
		return true
end		
alias :old_equal :==
def ==(other)
	return true if id == other.id
	return false unless no_loop(IdBag.new)
	self.old_equal other
end
end
	

p ([1,3,3,[3,4],"a"] == [1,3,3,[3,4],"a"]) 
p ([1,3,3,[3,4],"a"] == [1,3,3,[3,4],"b"])
a=[1]; a << a; b = [1] ; b << b; p b - a 
a =[1,2,3]; a << a; b =[1,2]; b << b;  a  << a; b << a; 
p a - b; p a; p b
c = [1,2,3]; c << c; b =[1,2]; b << b;  c  << c; b << c; 
p c - b, c, b
p c - a, a - c


def f(x); [x,x]; end
def g(x); f f f f f f f f x; end
def h(x); g g g g g g g g x; end
def i(x);  h h h h h h h h x; end
def j(x); i i i i  i i i   x;end

p ((f f  1) == (f f 1))


----------------------------
Recursion unprotected (corrected) flatten
not tested

class Array
def flatten
	res = []
	each do |i|
		if i.kind_of? Array 	
			res+= i.flatten
		else
			res << i
		end
	end
	res
end
end

----------------------------
Recursion limited hash value
not tested

class Object
def protected_hash(level)
    #ignore level
    hash
end
end

class Array
def protected_hash (level)
	return 0 if level < 1
	hsh = 0  	
	each do |i| 
		hsh ^= i.protected_hash (level- 1) 
  	end
	return hsh
end
def hash
	untangled_hash 5
end
end
----------------------------