From: dblack@... Date: 2007-09-05T07:15:08+09:00 Subject: Re: assigning to hash keys when there is a default value? Hi -- On Wed, 5 Sep 2007, Rick DeNatale wrote: > On 9/4/07, dblack@wobblini.net wrote: >>> >>> I for one, am glad that it works this way. The ruby idiom >>> >>> x ||= y >>> >>> is heavily used for lazy initialization/caching. While most often, >>> it's the rhs which is expensive to compute and therefore the thing we >>> want to short-circuit, since x= can in general be a method, and might >>> just be expensive, then optimizing the case where it boils down to x = >>> x as a nop, makes sense. >> >> I think The method always gets called, though: >> >> class C >> attr_reader :x >> def x=(n) >> puts "C#x=" >> true >> end >> end >> >> c = C.new >> c.x ||= 3 # C#x= >> >> *Unless*, of course, the object is a Hash which has either (a) a key >> corresponding to the indicated value, or (b) a default value with >> boolean truth value. > > No, in the case you posited, the assignment happened and C#x= got > called because c.x returned nil. Right; I got that wrong. >> Sigh. I really wish it were otherwise. What an annoying exception to >> the rule. > > Except that the 'rule' wasn't as you thought. The rule is that > > x ||= y > > is the same as > > x = y unless x OK, then: What an annoying exception to what should be the rule :-) >> Also, in the famous: >> >> h = Hash.new(1) >> h[5] ||= 10 >> >> case, it definitely isn't doing the "x = x" equivalent, since that >> would set the 5 key to 1. > > And that's as expected because h[5] returns the default value and > doesn't affect the state of the hash a whit, it doesn't create a 5 > key. If you want the default to affect the hash you need something > like > > hsh = Hash.new {|h,k| h[k] = 10} That one I didn't get wrong :-) I didn't say that retrieving the default value creates a key (which it doesn't, since the default value, whether nil or what you set it to, is specifically the default value for keys that don't exist). My point was that this: h = Hash.new(1) h[5] ||= 10 does not map to "x = x", assuming that x stands for h[5]. h[5] = h[5] *does* set a key; as I said, it would set the 5 key to 1. In fact this whole thread is really about the fact that hash defaults, which don't set keys, can be true, which short-circuits the ||= thing. I do think it's the only such case, and probably fairly edge, though obviously I'd like to see it do otherwise. David -- * Books: RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242) RUBY FOR RAILS (http://www.manning.com/black) * Ruby/Rails training & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)