Easiest way in Ruby to express "given this, set target to this if nil or undefined, else increment b

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

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

Also, I’d be able to use a succinct version of this without resorting
to globals.

An ideas?

Thanks in Advance,
Richard

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.

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. :wink:

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

On Jan 22, 10:50pm, RichardOnRails
[email protected] wrote:

class Z
def self.show_z
@@zz ||= 0; @@zz += 1
puts @@zz
end
end
Z.show_z # => 1
Z.show_z # => 2

The singleton pattern works well for this sort of thing.

require ‘singleton’ # this is part of Ruby’s standard library
class Counter
include Singleton
def count
@count = (@count ? @count + 1 : 0)
end
end

a = Counter.instance
a.count # => 0
a.count # => 1
b = Counter.instance
b.count # => 2

$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

And if you really need something like this (hopefully you don’t) try:

$h = Hash.new {|the_hash, a_key| the_hash[a_key] = 0}
def show_y
$h[:y] += 1
puts $h[:y]
end

and combining them:

require ‘singleton’
class Counters
include Singleton
def initialize
@counts = Hash.new {|h, k| h[k] = 0}
end
def get_count(sym)
@counts[sym] += 1
end
end

c = Counter.instance
c.get_count :y # => 1
c.get_count :y # => 2
c.get_count :z # => 1

On Jan 23, 12:26am, Jeremy B. [email protected] wrote:

Z.show_z # => 1
$h = Hash.new(:y)
TestOrEqual_operator.rb:18:in show_y': undefined method +’
doesn’t do anything because $h[:y] has a value of :y by default. The
at all??? You could just as easily do this:
Also, I’d be able to use a succinct version of this without resorting
in a class as you did in your first example.

-Jeremy

Hi Jeremy,

Thanks for that lucid explanation of where my error lay which in turn
made the + method inappropriate. The app I’m working do have class
and relevant methods defined, so using the scheme I playing with is
fine in a class context.

Again, thanks,
Richard

On Jan 23, 11:10am, yermej [email protected] wrote:

Z.show_z # => 2

end

end
end

c = Counter.instance
c.get_count :y # => 1
c.get_count :y # => 2
c.get_count :z # => 1

Thanks for introducing me to the Singleton class from the Ruby
library. I’m going to test whether that’ll be work best for my
current project.

Best wishes,
Richard