Changing Value of Ruby Variables/References

Changing value of ruby variables/references

my_var_a and my_var_set are different references, but they point at the same object. If you modify the object in my_var_set, the change shows up in my_var_a. However, if you repoint my_var_set at a new object, that doesn't change what my_var_a points at.

Edit: clarification...

What Ruby does is called passing references by value. When you say

my_var_a = "nothing happend to me"

Ruby saves the string "nothing happend to me" in a memory location (let's call it 1000), and saves the my_var_a reference in another memory location (let's say 2000). When your code uses my_var_a, the interpreter looks at location 2000, see that it points to 1000, then gets the actual string value from 1000.

When you call parse_set(my_var_a), Ruby actually creates a new reference named my_var_set and points it to the string that my_var_a was pointing at (memory location 1000). However, my_var_set is a copy of the my_var_a reference -- let's say my_var_set was created at memory location 3000. my_var_a and my_var_set are 2 completely different references in memory, they just happen to point at the same exact memory location which holds the string value.

The statement my_var_set = "my value changed" in parse_set creates a new string in memory and points my_var_set at that new memory location. However, this doesn't change what the original my_var_a reference points at! Now that my_var_set points at a different memory location, nothing that you do to that variable will affect my_var_a.

The same reference copy happens for parse_sub as well. But the reason that parse_sub changes the string is because you're calling a method directly on the my_var_sub reference. When you do this, the interpreter gets the object that my_var_sub is pointing at and then modifies it. So that change will show up in the my_var_a reference, because it still points at the same string.

Why can you change the value of a local variable in Ruby in a function using another method but not with an assignment operator?

The key here is that you can never change a Ruby object's core self, once it has been created it will always be the same object until garbage collected and destroyed. It is only possible to alter properties of the object.

You can, however, change variables or object references such as instance variables, constants, attr_accessor properties among other things.

So in this case:

def uppercase2(value)
value = "WILLIAM"
end

This reassigns the value local variable. It does nothing to the original object.

If you want to replace that text you need to use methods on the object to effect it, if supported. In this case there is a method:

def uppercase2(value)
value.replace("WILLIAM")
end

These are generally termed in-place modifications as in the object itself is manipulated rather than swapped for another object.

Variable changing value, ruby

When you say x = y in Ruby that creates a variable with a reference to exactly the same object. Any modifications to x will apply to y and vice-versa:

y = "test"
x = y

x[0] = "b"

x
# => "best"
y
# => "best"

You can tell because of this:

x.object_id == y.object_id
# => true

They're identical objects. What you want is to make a copy first:

x = y.dup
x[0] = "b"
x
# => "best"
y
# => "test"

This results in two independent objects:

x.object_id == y.object_id
# => false

So in your case what you need is to change it like:

orig_string = string.dup

Now that being said, often the best way to process things in Ruby is by using functions that return copies, not manipulating things in place. A better solution is this:

def scramble_string(string, positions)
(0...string.length).map do |p|
string[positions[p]]
end.join
end

scramble_string("abcd", [3, 1, 2, 0])
"dbca"

Note that's a lot more succinct than the version with string manipulation.

Is Ruby pass by reference or by value?

In traditional terminology, Ruby is strictly pass-by-value. But that's not really what you're asking here.

Ruby doesn't have any concept of a pure, non-reference value, so you certainly can't pass one to a method. Variables are always references to objects. In order to get an object that won't change out from under you, you need to dup or clone the object you're passed, thus giving an object that nobody else has a reference to. (Even this isn't bulletproof, though — both of the standard cloning methods do a shallow copy, so the instance variables of the clone still point to the same objects that the originals did. If the objects referenced by the ivars mutate, that will still show up in the copy, since it's referencing the same objects.)

Change values of variables in array

Numbers (such as Fixnum) in Ruby are immutable. You cannot change the underlying value.

Once you assign one = 1, it is not possible to change the value of one without a new assignment. When you do one += 1. You are actually assigning the new value 2 to the variable one; it's a whole new object.

You can see this more clearly by looking at the object_id (a.k.a. __id__):

one = 1
1.object_id # => 2
one.object_id # => 2
one += 1
one.object_id # => 5
2.object_id # => 5

Now in your Array#map! statement, you're not actually changing the one object. A reference to this object is stored in the array; not the actual variable. When you enumerate with map!, the object returned by the block is then stored in the internal reference location at the same position. Think of the first pass over map! similar to the following:

one = 1
one.object_id # => 2

arr = [one]

arr[0].object_id # => 2

arr[0] += 1 # You are re-setting the object at index 0
# not changing the original `one`'s value

arr[0] # => 2
arr[0].object_id # => 5

one # => 1
one.object_id # => 2

Since these Fixnum objects are immutable, there is no way to change their value. This is why you have to de-reference the result of your map back to the original values:

(one, two, three) = [1, 2, 3]
one.object_id # => 3
two.object_id # => 5
three.object_id # => 7

(one, two, three) = [one, two, three].map{|e| e += 1}
one.object_id # => 5
two.object_id # => 7
three.object_id # => 9

How to pass by reference in Ruby?

Ruby is strictly pass-by-value, which means references in the caller's scope are immutable. Obviously they are mutable within the scope, since you can assign to them after all, but they don't mutate the caller's scope.

a = 'foo'

def bar(b)
b = 'bar'
end

bar(a)

a
# => 'foo'

Note, however, that the object can be mutated even if the reference cannot:

a = 'foo'

def bar(b)
b << 'bar' # the only change: mutate the object instead of the reference
b = 'baz'
end

bar(a)

a
# => 'foobar'

If the object you get passed is immutable, too, then there is nothing you can do. The only possibilities for mutation in Ruby are mutating the reference (by assignment) or asking an object to mutate itself (by calling methods on it).

You can return an updated value from your method and have the caller assign that to the reference:

a = :foo

def bar(b)
:"#{a}bar"
end

c = bar(a)

c
# => :foobar

Or you can wrap the immutable object in a mutable one and mutate that mutable wrapper:

a = [:foo]

def bar(b)
b[0] = :bar
end

bar(a)

a
# => [:bar]

[This is really the same thing as the second example above.]

But if you can't change anything, then you are stuck.



Related Topics



Leave a reply



Submit