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_o...@optushome.com.au, on Mon, Mar 14, 2005 at 05:41:40AM +1100:
> Sam Roberts <srob...@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.
>
>
>(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.
E
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
>Quoting rub...@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.
E
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 - drb...@segment7.net - http://segment7.net
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04