Method variable that retains value between calls?

I’ve been unable to find this in searching. If it doesn’t exist, how do I approximate it?

Could you elaborate on what you mean? Maybe post some pseudo code.

  def some_code(akey,aval)
   if !defined?(ahash)
      ahash=Hash.new(0) 
      anum = 1
   end
   puts "anum and ahash:  #{anum}   #{ahash}"

  ahash[akey] += aval*anum
  anum*=3
  return ahash.sum { |k,v| v }

end

some_code(‘a’,4)
some_code(‘b’,6)
puts “Total of ‘a’ ‘b’ ‘c’: #{some_code(‘c’,10)}”

I’ve forgotten how to specify that I’m inserting code, so please excuse the formatting.

You use 3 apostrophes to delimit posted code.

I can’t see why this would be a problem… Can’t you just use an instance variable to maintain the state between calls?

Are you sure you don’t want a closure?

#! /usr/bin/env ruby
# frozen_string_literal: true

class MyFuncs
  def self.get_num()
    num = 0
    Proc.new {|x| num += 1; num}
  end
end

func2 = MyFuncs.get_num()
func3 = MyFuncs.get_num()

puts func2.call()
puts func2.call()
puts func2.call()
puts func3.call()

Thank you.
I will do that if it’s really necessary.
I’m an old lady whose first language was Fortran and I was hoping to avoid setting up a class…

I’m doing the problems in the Exercism Ruby Track purely for amusement, not aiming for mastery, but I’m glad that there are people out here to help.

The only exposure I had with Fortran was Problem Solving and Structured Programming in WATFIV(35 years ago). Still have the book.

1 Like

Generally, changing variables outside the scope of a method is considered as bad practice.

If you want to handle a stativ-variable, why not use an instance var?

def ahash
  if @ahash.nil?
     @ahash=Hash.new(0) 
     anum = 1
  end
  @ahash
end
def ahash= val
  @ahash = val
end
def some_code(akey,aval)
    puts "anum and ahash:  #{anum}   #{ahash}"
    ahash[akey] += aval*anum
    anum*=3
    ahash.sum { |k,v| v }
 end

Hi @thelmalu,

As said, Ruby is object oriented, class is something you will use a lot :slight_smile:

The difficulty here is you miss a concept: the scopes. I hope this post will help you:
Variable Scope and Visibility”.

To summarize, you have 4 scopes in Ruby:

  • Global: variable start with ‘$’. Visible across all your application. Be aware than constants (start with a capital) are global. If you wonder why a class or module start with a capital, it’s because Ruby assign it to a constant.
  • Instance: variable start with ‘@’. Visible into a living object (as an instance of a class).
  • Locale: any other variables. Only visible within the current context.

The last is the class variable, start with ‘@@’ and can be a nightmare because its use-case is special. Don’t use it for now.

Okay, now your example.

You try to reuse a locale variable into your method. But this variable will be gone when the method returns. You will create a new local variable for each call.

The best solution for you is to use an object to track the content of your “ahash” and “anum” values.

If you think an object is overkill for you, you can use an anonymous function. Perhaps it’s a bit premature to introduce you to this, but it’s really a simple concept. We call it a “lambda”.

The advantage of a lambda is than you have the same rule as with a method but a lambda live in the context of where it was created. So, you can access any variable declared beforehand.

Because lambda is an object, you can’t call it directly as with a method. You must use “.call” to fire the code into it.

Ready for a try? Open an irb console and copy-past the code:

ahash = Hash.new(0) 
anum = 1

some_code = lambda do |akey, aval|
  puts "anum and ahash:  #{anum}   #{ahash}"

  ahash[akey] += aval*anum
  anum*=3
  ahash.sum { |k,v| v }
end

Now, test our code. Don’t foget a lambda (or a proc) need to use the method call:

some_code.call("a", 4)
# anum and ahash:  1   {}
# => 4

some_code.call("b", 6)
# anum and ahash:  3   {"a"=>4}
# => 22

ahash
# => {"a"=>4, "b"=>18}

That’s it.

But the most usual way is to play with object. It’s not much efforts:


class MyObject
  def initialize
    @ahash = Hash.new(0) 
    @anum = 1
  end

  def some_code(akey, aval)
    puts "anum and ahash:  #{@anum}   #{@ahash}"

    @ahash[akey] += aval*@anum
    @anum*=3
    @ahash.sum { |k,v| v }
  end
end

And play with it:

my_object = MyObject.new

my_object.some_code("a", 4)
# anum and ahash:  1   {}
# => 4

my_object.some_code("b", 6)
# anum and ahash:  3   {"a"=>4}
# => 22

It’s ok too. What is the internal state of your object?

puts my_object.inspect
# <MyObject:0x000055984d84e690 @ahash={"a"=>4, "b"=>18}, @anum=9>

Keep in mind than any method / lambda / proc / block will return the last expression. No need to explicitly use return because your last line will always return.

Have enjoy in your coding :slight_smile:

1 Like

Thank you. This is very clear and helpful. I knew about lambda, but didn’t realize it could be used in this way.
I will, however, take everyone’s advice, and create a class object. Otherwise, what I’m really doing is trying to reduce Ruby to an emulation of my mid-1960’s Fortran.

1 Like

Maybe this will help? In this code I’m using readline instead of the gets method for a basic repl.

CODE:
require ‘readline’

def phase_color(circuit)
a = circuit.to_i % 6
if a == 0
puts “blue/yellow”
end

 if a == 1
 puts "black/brown"
 end

 if a == 2
 puts "black/brown"
 end
 
 
 if a == 3
 puts "red/orange"
 
 end
 if a == 4
 puts "red/orange"
 end
  if a ==5
  puts "blue/yellow"
  end
  
  if a == 6
  puts "blue/yellow"
  end
 
puts a

end

This method is very basic. I had to use multiple if statments because I used dcoder on my phone.

This program I made to help me identify phase colors in the American standard. The formula is simple. The circuit number divided by 6. I don’t need anything but the remainder. So circuit.to_i % 6 will divide my input number and only return the remainder value.

The second half of the code:

while input = Readline.readline("circuit number: ")
phase_color(input)
exit(0)
break if input == “exit”

end

This part of the code gives the user a prompt. This case, a numeric input. It takes the user input and updates the circuit argument for phase_color, then runs the algorithm within the a variable of the method. Depending on the return of variable a, the if statement that is true will be returned.