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

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs