From: Jeremy Bopp Date: 2011-01-23T14:26:24+09:00 Subject: Re: Easiest way in Ruby to express "given this, set target to this if nil or undefined, else increment by this" On 01/22/2011 10:50 PM, RichardOnRails wrote: > The following works, but I'd prefer not to have to class values: > > class Z > def self.show_z > @@zz ||= 0; @@zz += 1 > puts @@zz > end > end > Z.show_z # => 1 > Z.show_z # => 2 Replace the @@'s with @'a so that you use a class instance variable instead. In this case it works effectively the same while avoiding the nasty class variables. > I tried this: > > $h = Hash.new(:y) > def show_y > $h[:y] ||= 0; $h[:y] += 1 # => Error msg # Line 18 > puts $h[:y] > end > show_y > show_y > > but it doesn't pass muster with Ruby 1.8.6. I get a complaint about > the first line in show_y, which makes no sense IMHO: > > TestOrEqual_operator.rb:18:in `show_y': undefined method `+' > for :y:Symbol (NoMethodError) > from TestOrEqual_operator.rb:21 The error is ultimately the result of the way you initialized your Hash instance. By passing :y to the new method, you arranged for any uninitialized key referenced within the hash to have the symbol :y as a value. Therefore, $h[:y] ||= 0 doesn't do anything because $h[:y] has a value of :y by default. The next statement then attempts to increment the value of $h[:y] by 1 and fails because that value is :y and the + method is not defined for Symbols. If what I'm saying isn't exactly clear, try this: h = Hash.new(:howdy) puts h['Say hello like a Texan!'] puts h['Now say hello like a John Wayne!'] You could avoid this particular problem by simply setting $h to {} rather than calling Hash.new(:y). But why is a hash necessary for this at all??? You could just as easily do this: def show_y $y ||= 0 $y += 1 puts $y end This still leaves you using a global, but it's much simpler. More importantly, it works. ;-) > Also, I'd be able to use a succinct version of this without resorting > to globals. Without knowing more about the problem you're trying to solve, it's hard to provide a better response. Apparently, you want to call a method that automatically initializes some state when needed and then modify and preserve that state between subsequent calls to that method. That state, be it a number as in your examples here or something else, has to be stored somewhere. I'm not a fan of globals in general, so I would choose to keep the state in a class as you did in your first example. -Jeremy