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
Ruby String with Usd "Money" Converted to Number
Warning: String Literal in Condition
Running Parallel Selenium Tests with Capybara
To Make an API Call with an Oauth Token
Ruby - Array.Join Versus String Concatenation (Efficiency)
Sunspot_Rails Gem - " Errno:: Econnrefused (Connection Refused - Connect (2)) "
Map an Array Modifying Only Elements Matching a Certain Condition
How to Run a Simple Ruby Script in Any Web Server (Apache or Mongrel or Any Thing Else)
Adding a "Like/Unlike" Button to a Post in Rails
How to Use the Ruby "Self" Keyword
Does Activemerchant Support Subscription Based Transaction