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

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

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. :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 01/22/2011 10:50 PM, RichardOnRails 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 22, 10:50 pm, RichardOnRails <RichardDummyMailbox58...@USComputerGurus.com> wrote:

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, 12:26 am, Jeremy Bopp <jer...@bopp.net> wrote:

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

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

···

On Jan 23, 11:10 am, yermej <yer...@gmail.com> wrote:

On Jan 22, 10:50 pm, RichardOnRails > > <RichardDummyMailbox58...@USComputerGurus.com> 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