[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Re: how do I convince Hash that two keys are the same?

Sam Roberts

3/13/2005 8:47:00 PM

(I sent this to ruby-doc by accident, and got a response, but I am
reposting to ruby-talk, sorry for the cross-post.)

Thanks, thats very informative, and I would not have guessed.

I tried a different approach, I derived from String. This has the
very odd behaviour of blowing the Stack!

class Str < String
def hash; self.downcase.hash; end # <- this doesn't look recursive
# to me, but it blows the stack
end

h = { }

k = Str.new('a')

k == 'A'
'A' == k

h[k] = 'lower'

k = Str.new('A')
p k.downcase # Should return an instance of String, right?
p k.downcase.hash # <--- this line cause stack overflow
k.hash == 'a'.hash

h.has_key? k


I think Str#downcase should return the same thing as String#downcase =>
a String, and not a Str, so I don't see why there is a recursive call,
here.

What am I missing?

Thanks,
Sam

Quoting g_ogata@optushome.com.au, on Mon, Mar 14, 2005 at 05:41:40AM +1100:
> Sam Roberts <sroberts@uniserve.com> writes:
>
> > I've tried:
> >
> >
> > h = { }
> >
> > k = 'a'
> >
> > class << k
> > def hash; self.downcase.hash; end
> > def ==(s); self.downcase == s.downcase; end
> > def eql?(s); self == s; end
> > def ===(s); self == s; end
> > end
> >
> > k = 'A'
> >
> > class << k
> > def hash; self.downcase.hash; end
> > def ==(s); self.downcase == s.downcase; end
> > def eql?(s); self == s; end
> > def ===(s); self == s; end
> > end
> >
> > k == 'a'
> > k.hash == 'a'.hash
> >
> > # I want this to be true!
> > h.has_key? k
> >
> > But it doesn't work. I'm particularly confused because if I write a
> > class Str that wraps String and defines those methods things do work
> > out...
> >
> > Any pointers?
>
> This looks like a consequence of the fact that when ruby takes the
> hash of a key, if the key is a String, it uses the default
> implementation of String#hash directly, even if String#hash has been
> redefined, or the object has a singleton class. (The same thing is
> true with Fixnums and Symbols.)
>
> Try:
>
> class String
> def hash
> puts '!'
> super
> end
> end
>
> {}['a'] = 1
>
> Then change the String to an Object.
>
> HTH.
>
>


4 Answers

ES

3/13/2005 9:09:00 PM

0


In data 3/13/2005, "Sam Roberts" <sroberts@uniserve.com> ha scritto:

>(I sent this to ruby-doc by accident, and got a response, but I am
>reposting to ruby-talk, sorry for the cross-post.)
>
>Thanks, thats very informative, and I would not have guessed.
>
>I tried a different approach, I derived from String. This has the
>very odd behaviour of blowing the Stack!
>
> class Str < String
> def hash; self.downcase.hash; end # <- this doesn't look recursive
> # to me, but it blows the stack
> end
>
> h = { }
>
> k = Str.new('a')
>
> k == 'A'
> 'A' == k
>
> h[k] = 'lower'
>
> k = Str.new('A')
> p k.downcase # Should return an instance of String, right?
> p k.downcase.hash # <--- this line cause stack overflow
> k.hash == 'a'.hash
>
> h.has_key? k
>
>
>I think Str#downcase should return the same thing as String#downcase =>
>a String, and not a Str, so I don't see why there is a recursive call,
>here.

When you derive, you establish an is_a relationship. Str is_a String.
All methods are also derived so you're actually calling Str#downcase,
although the code being executed was defined in String.

You'd possibly want to alias hash to old_hash and then call
self.downcase.old_hash, which seems to be what you're trying
to do.

>What am I missing?
>
>Thanks,
>Sam
>
>Quoting g_ogata@optushome.com.au, on Mon, Mar 14, 2005 at 05:41:40AM +1100:
>> Sam Roberts <sroberts@uniserve.com> writes:
>>
>> > I've tried:
>> >
>> >
>> > h = { }
>> >
>> > k = 'a'
>> >
>> > class << k
>> > def hash; self.downcase.hash; end
>> > def ==(s); self.downcase == s.downcase; end
>> > def eql?(s); self == s; end
>> > def ===(s); self == s; end
>> > end
>> >
>> > k = 'A'
>> >
>> > class << k
>> > def hash; self.downcase.hash; end
>> > def ==(s); self.downcase == s.downcase; end
>> > def eql?(s); self == s; end
>> > def ===(s); self == s; end
>> > end
>> >
>> > k == 'a'
>> > k.hash == 'a'.hash
>> >
>> > # I want this to be true!
>> > h.has_key? k
>> >
>> > But it doesn't work. I'm particularly confused because if I write a
>> > class Str that wraps String and defines those methods things do work
>> > out...
>> >
>> > Any pointers?
>>
>> This looks like a consequence of the fact that when ruby takes the
>> hash of a key, if the key is a String, it uses the default
>> implementation of String#hash directly, even if String#hash has been
>> redefined, or the object has a singleton class. (The same thing is
>> true with Fixnums and Symbols.)
>>
>> Try:
>>
>> class String
>> def hash
>> puts '!'
>> super
>> end
>> end
>>
>> {}['a'] = 1
>>
>> Then change the String to an Object.
>>
>> HTH.

E



Sam Roberts

3/13/2005 9:50:00 PM

0

Quoting ruby-ml@magical-cat.org, on Mon, Mar 14, 2005 at 06:08:35AM +0900:
> >I think Str#downcase should return the same thing as String#downcase =>
> >a String, and not a Str, so I don't see why there is a recursive call,
> >here.
>
> When you derive, you establish an is_a relationship. Str is_a String.
> All methods are also derived so you're actually calling Str#downcase,
> although the code being executed was defined in String.

I don't think thats the answer, its not how things normally work.

String#to_s doesn't work that way, for example, despite the docs saying
it does, it returns an object of class String, even if the receiver
isn't of class String.

It looks like String.upcase is creating a new instance of its derived
class, but somehow bypassing the derived classes initialize, see example
below.


I don't understand how it does this. I thought it might be calling
self.class.new, but it doesn't.

For example, how would I do this so it has the same behaviour as
#downcase, returning an object of the derived class:


class String
def brackets
self.class.new("(" + self + ")")
end
end

The above doesn't work, so how would I do it? Hm, maybe self.copy.tr(..)
is what its doing...

I guess I should read the String src code.

Cheers,
Sam


Run the following through irb.


class String
def brackets
self.class.new("(" + self + ")")
end
end

class Str < String
def initialize(s)
puts '!Str'
super(s)
end
end


class Now < String
def initialize
puts '!Now'
super(Time.now.to_s)
end
end

class Now2 < String
def initialize(a,b)
puts "!Now2 #{a} #{b}"
super(Time.now.to_s)
end
end

Str.new('aa').upcase
Str.new('aa').upcase.class
Now.new.upcase
Now.new.upcase.class
Now2.new(1,2).upcase
Now2.new(1,2).upcase.class

Str.new('aa').brackets
Now.new.brackets
Now2.new(1,2).brackets


Str.new('aa').to_s.class
Now.new.to_s.class
Now2.new(1,2).to_s.class




ES

3/13/2005 10:01:00 PM

0


In data 3/13/2005, "Sam Roberts" <sroberts@uniserve.com> ha scritto:

>Quoting ruby-ml@magical-cat.org, on Mon, Mar 14, 2005 at 06:08:35AM +0900:
>> >I think Str#downcase should return the same thing as String#downcase =>
>> >a String, and not a Str, so I don't see why there is a recursive call,
>> >here.
>>
>> When you derive, you establish an is_a relationship. Str is_a String.
>> All methods are also derived so you're actually calling Str#downcase,
>> although the code being executed was defined in String.
>
>I don't think thats the answer, its not how things normally work.

static VALUE
rb_str_downcase(str)
VALUE str;
{
str = rb_str_dup(str);
rb_str_downcase_bang(str);
return str;
}

VALUE
rb_str_dup(str)
VALUE str;
{
VALUE dup = str_alloc(rb_obj_class(str));
rb_str_replace(dup, str);
return dup;
}

It may not seem intuitive but it's conceptually exactly how it's
supposed to go.

>String#to_s doesn't work that way, for example, despite the docs saying
>it does, it returns an object of class String, even if the receiver
>isn't of class String.
>
>It looks like String.upcase is creating a new instance of its derived
>class, but somehow bypassing the derived classes initialize, see example
>below.
>
>
>I don't understand how it does this. I thought it might be calling
>self.class.new, but it doesn't.
>
>For example, how would I do this so it has the same behaviour as
>#downcase, returning an object of the derived class:
>
>
> class String
> def brackets
> self.class.new("(" + self + ")")
> end
> end
>
>The above doesn't work, so how would I do it? Hm, maybe self.copy.tr(..)
>is what its doing...
>
>I guess I should read the String src code.
>
>Cheers,
>Sam
>
>
>Run the following through irb.
>
>
>class String
> def brackets
> self.class.new("(" + self + ")")
> end
>end
>
>class Str < String
> def initialize(s)
> puts '!Str'
> super(s)
> end
>end
>
>
>class Now < String
> def initialize
> puts '!Now'
> super(Time.now.to_s)
> end
>end
>
>class Now2 < String
> def initialize(a,b)
> puts "!Now2 #{a} #{b}"
> super(Time.now.to_s)
> end
>end
>
>Str.new('aa').upcase
>Str.new('aa').upcase.class
>Now.new.upcase
>Now.new.upcase.class
>Now2.new(1,2).upcase
>Now2.new(1,2).upcase.class
>
>Str.new('aa').brackets
>Now.new.brackets
>Now2.new(1,2).brackets
>
>
>Str.new('aa').to_s.class
>Now.new.to_s.class
>Now2.new(1,2).to_s.class

E



Eric Hodel

3/13/2005 10:26:00 PM

0

Please don't top post. With all the stuff in this thread, I find it
nearly impossible to figure out what you have and haven't tried.

On 13 Mar 2005, at 12:46, Sam Roberts wrote:

> (I sent this to ruby-doc by accident, and got a response, but I am
> reposting to ruby-talk, sorry for the cross-post.)
>
> Thanks, thats very informative, and I would not have guessed.
>
> I tried a different approach, I derived from String. This has the
> very odd behaviour of blowing the Stack!
>
> class Str < String
> def hash; self.downcase.hash; end # <- this doesn't look recursive
> # to me, but it blows the stack
> end
>
> h = { }
>
> k = Str.new('a')
>
> k == 'A'
> 'A' == k
>
> h[k] = 'lower'
>
> k = Str.new('A')
> p k.downcase # Should return an instance of String, right?
> p k.downcase.hash # <--- this line cause stack overflow
> k.hash == 'a'.hash
>
> h.has_key? k
>
> What am I missing?

To store into the same slot of a Hash, an object must have the same
#hash as the object it would replace and be #eql?.

--
Eric Hodel - drbrain@segment7.net - http://se...
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04