From: dblack@... Date: 2007-09-05T03:36:25+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/3/07, Robert Klemme wrote: >> 2007/9/3, dblack@wobblini.net : > >>> x ||= y is, I think, always supposed to be exactly equivalent to >>> x = x || y, ... > >> I can't point my finger on it but I believe x||=y is equivalent to >> "x=y unless x" instead of "x=x||y". It seems to be more reasonable to >> skip the assignment altogether if the value is true equivalent >> already. That would also explain behavior much better. :-) > > Robert, > > Although I can't find the documentation quickly, although I'm 95% > certain that it should be in the pickaxe somewhere, I'm pretty sure > that you are correct. > > I've just looked at parse.y and eval.c for ruby1.8.6 and it would appear that: > > h[2] ||= 10 > > gets compiled to a NODE_OP_ASGN_OR node with h[2] as the lhs and 10 as > the rhs. Here's the code from eval.c which evaluates such a node: > > case NODE_OP_ASGN_OR: > if ((node->nd_aid && !is_defined(self, node->nd_head, 0)) || > !RTEST(result = rb_eval(self, node->nd_head))) { > node = node->nd_value; > goto again; > } > break; > > So what happens is that the lhs is only evaluated if the the lhs > (node->nd_head) is not defined || it evaluates to an untrue value. > > In the case of h[5] the default value for the hash means that it will > evaluate to 5, and the assignment is not done. > > 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. Sigh. I really wish it were otherwise. What an annoying exception to 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. I don't know.... However many times I look at it, I just can't see this: h[5] ||= 10 as *not* meaning that I expect h to end up having a 5 key, one way or another. 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)