0

Background

I have a method which in which I want to modify a number which will be passed into the method, but not returned.

Example

def increase_number_by_one_and_do_some_other_stuff(number)
    number = number + 1
    some_other_stuff = "Other stuff I want to happen in this function"
    some_other_stuff
end

number = 1

increase_number_by_one_and_do_some_other_stuff(number)

puts number
# => 1
## Expecting 2

Is there any way to achieve this in Ruby, or is it always Pass by Value? I have tried to understand exactly how object references/values are treated in Ruby, but for my level of experience it's quite confusing still.

More context

The situation I'm trying to use it is something like this. Tried to keep it simple but still give enough information.

total = 1000
unaccounted_for = 100
accounted_for = total - unaccounted_for

distribute_unaccounted(unaccounted_for, accounted_for)

puts unaccounted_for
# => 55
puts accounted_for
# => 945

def distributed_unaccounted(unaccounted_for, accounted_for)
   # lots of code
   category_a_reaccounted = 25 # calculated within this method
   category_b_reaccounted = 20 # calculated within this method
   total_reaccounted = category_a_reaccounted + category_b_reaccounted
   unaccounted_for -= total_reaccounted
   accounted_for += total_reaccounted
end

Chris A
  • 863
  • 1
  • 10
  • 31
  • 1
    Ruby is always pass-by-value. The `number` variable within the method exists in a separate variable scope. Assigning a new object to the variable via `number = number + 1` is limited to the method. Variables outside the method aren't affected. – Stefan Feb 19 '20 at 10:54
  • With local variables it's impossible, you can use instance variable, for example, `@number` and then in method `@number = @number+1`(`@number += 1`) or you can return `number` in the end of method and then `number = increase_number_by_one_and_do_some_other_stuff(number)` – Oleksandr Holubenko Feb 19 '20 at 10:55

1 Answers1

1

I have a method which in which I want to modify a number ...

Numbers are immutable, you cannot change them. 1 will always be 1.

I think what you actually want is to assign a new value to the number variable within your method and the number variable outside your method.

But that's not possible in Ruby. The variables inside your methods have their own variable scope and changes don't propagate outside. So assigning a new object to a variable inside your method won't affect "outside" variables.

Is there any way to achieve this in Ruby ...

What I see are several values and methods that work on those values. Why not move them into a class, as instance variables and instance methods?

class Foo
  attr_reader :total, :unaccounted_for

  def initialize(total, unaccounted_for)
    @total = total
    @unaccounted_for = unaccounted_for
  end

  def accounted_for
    total - unaccounted_for
  end

  def category_a_reaccounted
    25 # actually calculated
  end

  def category_b_reaccounted
    20 # actually calculated
  end

  def distributed_unaccounted
    total_reaccounted = category_a_reaccounted + category_b_reaccounted
    @unaccounted_for -= total_reaccounted
  end
end

Usage, based on your example:

foo = Foo.new(1000, 100)

foo.unaccounted_for #=> 100
foo.accounted_for   #=> 900

foo.distributed_unaccounted

foo.unaccounted_for #=> 55
foo.accounted_for   #=> 945
Stefan
  • 109,145
  • 14
  • 143
  • 218
  • "Why not move both into a class, as instance variable and instance method?" Well, it works...but it seems a huge hassle just to increment a number. – Chris A Feb 19 '20 at 11:53
  • @ChrisA I assumed that number and method are closely related, that's why I suggested to move them into their own class. Maybe you could give a bit more context / meaning. What is that number and what is the method supposed to do? Why is the number incremented within the method? – Stefan Feb 19 '20 at 12:14
  • @ChrisA thanks for the additional information. It seems as if you do much more than just incrementing a number. I would definitely recommend to encapsulate the related data and calculations in a class, see my updated example. – Stefan Feb 19 '20 at 14:39