From: Robert Klemme Date: 2007-09-07T06:10:09+09:00 Subject: Re: assigning to hash keys when there is a default value? On 05.09.2007 00:15, dblack@wobblini.net wrote: > > 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. I believe it's not the only case. Although x= and []= are different, it seems in *both* cases assignment is not even invoked for ||=: irb(main):001:0> class Foo irb(main):002:1> def x irb(main):003:2> p "x" irb(main):004:2> @x irb(main):005:2> end irb(main):006:1> def x=(v) irb(main):007:2> p "x=" irb(main):008:2> @x=v irb(main):009:2> end irb(main):010:1> def [](k) irb(main):011:2> p "[]" irb(main):012:2> k irb(main):013:2> end irb(main):014:1> def []=(k,v) irb(main):015:2> p "[]=" irb(main):016:2> k irb(main):017:2> end irb(main):018:1> end => nil irb(main):019:0> f=Foo.new => # irb(main):020:0> f.x||=10 "x" "x=" => 10 irb(main):021:0> f.x||=20 "x" => 10 irb(main):022:0> f.x=30 "x=" => 30 irb(main):023:0> f[10]||=20 "[]" => 10 irb(main):024:0> f[nil]||=30 "[]" "[]=" => 30 irb(main):025:0> f[20]=30 "[]=" => 30 irb(main):026:0> Kind regards robert