a = "test"
b = a
b << "test1"
puts a
puts b
b += "test2"
puts a
puts b
Output
------
$ruby main.rb
testtest1
testtest1
testtest1
testtest1test2
I was expecting following output.
test
testtest1
test
testtest1test2
b and a holding the same object through your assignment b = a.Thus while you are modifying b with b << "test1", it is actually the same object which is pointing by a.
a = "test"
b = a
a.object_id # => 71753220
b.object_id # => 71753220
Thus, I can tell you behavior you are seeing is correct.
Now b += "test2", here you are assigning a new object to b.
b += "test2"
a.object_id # => 71753220
b.object_id # => 72602390
Better to read
String#+
Concatenation—Returns a new String containing other_str concatenated to str
String#<<
Append—Concatenates the given object to str.
Related
can you please advice me why array_words includes after abc input three times cab string? I expect that includes abc, bac, cab ...
str1 = gets.chomp
start_position = 0
max = str1.length - 1
array_words = Array.new
def get_str1(str1, max, start_position, array_words)
for x in start_position..max
pom = str1[start_position]
str1[start_position] = str1[x]
str1[x] = pom
puts "Inside loop: " + str1
array_words << str1
end
puts array_words
end
In simple words: array_words contain 3 pointers to one object str1 which in the last iteration is 'cab'. You can test it yourself
puts "Inside loop: " + str1.object_id.to_s
array_words << str1
end
puts array_words.map(&:object_id)
Simpliest method is to create another object when adding to array:
array_words << str1.dup
Because array_words contains multiple references to the same object.
Minimal reproduction example:
[1] pry(main)> str1 = "abc"
=> "abc"
[2] pry(main)> array_words = []
=> []
[3] pry(main)> array_words << str1
=> ["abc"]
[4] pry(main)> str1[0] = 'x'
=> "x"
[5] pry(main)> array_words
=> ["xbc"]
You are appending the same object into the array each time, and then you are mutating the object.
An easy fix here would be to explicitly create a new object each time you append to the array:
array_words << str1.dup
(See: Object#dup.)
On a separate note, whilst there's nothing inherently wrong with your approach, it's generally considered un-rubyish to use for loops. This language almost always provides "nicer" ways to implement things. For example, how about:
(0..max).map do |i|
str1.slice(i, str1.length-i) + str1.slice(0, i)
end
# => ["abc", "bca", "cab"]
The following code:
test_1 = "my test string"
test_2 = test_1
test_2[3] = "B"
puts test_1
puts test_2
test_1 = "my test string"
test_2 = test_1
test_2 = test_2 + ""
test_2[3] = "B"
puts test_1
puts test_2
produces the following result:
my Best string
my Best string
my test string
my Best string
Can some explain to me why in the first case both strings are altered (as though test_2 is being assigned by reference) but in the second case only test_2 changes (as though by value)?
Here is the clarification :
test_1 = "my test string"
test_2 = test_1
test_2.object_id # => 83607730
test_1.object_id # => 83607730
test_2 = test_2 + ""
test_2.object_id # => 83606390
test_1.object_id # => 83607730
String#+ created a new object.
Thus test_2[3] = "B" added "B" to the new object referenced by test_2.It wouldn't change the object,whose reference is horded by test_1. Which is not the case in below :
test_1 = "my test string"
test_2 = test_1
test_2.object_id # => 70466640
test_1.object_id # => 70466640
Here test_2 and test_1 both holds the reference to the same object. Thus when you are doing test_2[3] = "B",it is basically doing change in the same object which is referenced by the local variables test_1 and test_2.
When you declare "test" in Ruby, this creates a new String object.
"test".object_id # => 9157960
"test".class # => String
And when you assign a string to a variable, you point the variable to the string object on the other side of the assignment.
Take a look at the following:
# Point variable to first String object.
a = "test" # => "test"
a.object_id # => 9307800
# Point variable to second String object.
b = "test" # => "test"
b.object_id # => 9307760
a.equal?(b) # => false
# Point variable to first String object.
c = a # => "test"
c.object_id # => 9307800
a.equal?(c) # => true
So when you manipulate a here, you manipulate the object where c points.
I am new to Ruby and to this site.
The following two functions are different, one alters the variable outside the function and one does not.
def m1 (x)
x << "4"
end
def m2 (x)
x = x + "4"
end
str="123"
m2(str) #str remains unchanged 123
m1(str) #str is changed to 1234
I would like to make sure I understand this correctly -
When m1 is called, the reference to str is copied and passed to the function which sees it as x. Operator << changes x which references the origial str so str is changed by this operation.
When m2 is called, the reference to str is copied and passed to the function which sees it as x. Operator + creates a new string, and the assignment x = x + "4" simply redirects x to the new string leaving the original str variable untouched.
Right?
Thanks
String#+ :: str + other_str → new_str Concatenation—Returns a new String containing other_str concatenated to str.
String#<< :: str << integer → str : Append—Concatenates the given object to str.
<< doesn't create the new object, where as + does.
a = "str"
#=> "str"
a.object_id
#=> 14469636
b = a << "ing"
#=> "string"
a.object_id
#=> 14469636
b.object_id
#=> 14469636
a= "str"
#=> "str"
b = a + "ing"
#=> "string"
a.object_id
#=> 16666584
b.object_id
#=> 17528916
EDIT
From your comment, got your point. See below:
def m1 (x)
x << "4"
end
#=> nil
def m2 (x)
x = x + "4"
end
#=> nil
str="123"
#=> "123"
m2(str)
#=> "1234"
str
#=> "123"
Here str didn't change as you passed the value inside the method m2(), all the changes local to the method as per the above call. Thus below str not showing that change.To see the change you have to call it as below.
str = m2(str)
#=> "1234"
str
#=> "1234"
OR
You could do the stuff as below :- where I passed reference address of str but not the value.
str = "abc"
#=> "abc"
str.object_id
#=> 16250484
ObjectSpace._id2ref(16250484)
#=> "abc"
def m1 (x)
ObjectSpace._id2ref(x) << "4"
end
#=> nil
m1(16250484)
#=> "abc4"
str
#=> "abc4"
Hope it make sense :)
Cheers!
<< the concatenate operator is destructive to a string. This means that it will manipulate the variable it acts upon, not just return the result of the expression.
example:
str = "abc"
puts str + "d" # "abcd"
puts str # "abc"
puts str << "d" # "abcd"
puts str # "abcd"
The following two functions are different, one alters the variable outside the function and one does not.
This is wrong. Neither of the two methods (they are methods, BTW, not functions, Ruby doesn't have functions; there is a fundamental difference) alters the str variable. m1 modifies the object the variable points to, but that is completely different from modifying the variable itself.
To give a little context around how I understand the problem.
Using splat collect on a string sends :to_a or :to_ary to the String
class String
def method_missing method, *args, &block
p method #=> :to_ary
p args #=> []
p block #=> nil
end
end
*b = "b"
So I was thinking that redefining the :to_ary method would be what I'm after.
class String
def to_ary
["to_a"]
end
end
p *a = "a" #=> "a"
p a #=> "a"
*b = "b"
p b #=> ["to_a"]
Now this confuses me to no end.
Printing the result from the *a = "a" changes the value assigned to a?
To demonstrate further
class String
def to_ary
[self.upcase!]
end
end
p *a = "a" #=> "a"
p a #=> "a"
*b = "b"
p b #=> ["B"]
Very interesting question! Ruby takes this expression:
p *a = "a"
and translates it to something like this:
temp = (a = "a")
p *temp
So the first thing that happens is that a gets assigned to "a", and then the result of the assignment expression which is "a" gets splatted and sent to p. Since p's default behaviour when sent multiple arguments is just to iterate over and print each one, you only see "a" appear.
In short, it follows a "assign then splat" order of evaluation. So a gets assigned to "a" before the string gets splatted.
When you don't have a function call however, it is interpreted as something like this:
# *a = "a" gets interpreted as:
temp = "a"
a = *temp
This follows a "splat then assign" order of evaluation. So a gets assigned after the string gets splatted.
You can see what's being received by a function by going like this:
def foo *args
puts args.inspect
end
foo *a = "a" # outputs ["a"]
a # outputs "a"
Hope this clears up what's going on!
In short (thanks to Mark Reed):
p *a = "a" # interpreted as: p(*(a = "a"))
*a = "a" # interpreted as: a = *("a")
>> a = 5
=> 5
>> b = a
=> 5
>> b = 4
=> 4
>> a
=> 5
how can I set 'b' to actually be 'a' so that in the example, the variable a will become four as well. thanks.
class Ref
def initialize val
#val = val
end
attr_accessor :val
def to_s
#val.to_s
end
end
a = Ref.new(4)
b = a
puts a #=> 4
puts b #=> 4
a.val = 5
puts a #=> 5
puts b #=> 5
When you do b = a, b points to the same object as a (they have the same object_id).
When you do a = some_other_thing, a will point to another object, while b remains unchanged.
For Fixnum, nil, true and false, you cannot change the value without changing the object_id. However, you can change other objects (strings, arrays, hashes, etc.) without changing object_id, since you don't use the assignment (=).
Example with strings:
a = 'abcd'
b = a
puts a #=> abcd
puts b #=> abcd
a.upcase! # changing a
puts a #=> ABCD
puts b #=> ABCD
a = a.downcase # assigning a
puts a #=> abcd
puts b #=> ABCD
Example with arrays:
a = [1]
b = a
p a #=> [1]
p b #=> [1]
a << 2 # changing a
p a #=> [1, 2]
p b #=> [1, 2]
a += [3] # assigning a
p a #=> [1, 2, 3]
p b #=> [1, 2]
You can't. Variables hold references to values, not references to other variables.
Here's what your example code is doing:
a = 5 # Assign the value 5 to the variable named "a".
b = a # Assign the value in the variable "a" (5) to the variable "b".
b = 4 # Assign the value 4 to the variable named "b".
a # Retrieve the value stored in the variable named "a" (5).
See this article for a more in-depth discussion of the topic: pass by reference or pass by value.
As has been noted the syntax you are using can not be done. Just throwing this out there though you could make a wrapper class it depends what you actually want to do
ruby-1.8.7-p334 :007 > class Wrapper
ruby-1.8.7-p334 :008?> attr_accessor :number
ruby-1.8.7-p334 :009?> def initialize(number)
ruby-1.8.7-p334 :010?> #number = number
ruby-1.8.7-p334 :011?> end
ruby-1.8.7-p334 :012?> end
=> nil
ruby-1.8.7-p334 :013 > a = Wrapper.new(4)
=> #<Wrapper:0x100336db8 #number=4>
ruby-1.8.7-p334 :014 > b = a
=> #<Wrapper:0x100336db8 #number=4>
ruby-1.8.7-p334 :015 > a.number = 6
=> 6
ruby-1.8.7-p334 :016 > a
=> #<Wrapper:0x100336db8 #number=6>
ruby-1.8.7-p334 :017 > b
=> #<Wrapper:0x100336db8 #number=6>
You can use arrays:
a = [5]
b = a
b[0] = 4
puts a[0] #=> 4
This idea is based on this answer.
Just for the sake of reference.
>> a = 5
=> 5
>> a.object_id
=> 11
>> b = a
=> 5
>> b.object_id
=> 11
>> b = 4
=> 4
>> b.object_id
=> 9
>> a.object_id
=> 11
# We did change the Fixnum b Object.
>> Fixnum.superclass
=> Integer
>> Integer.superclass
=> Numeric
>> Numeric.superclass
=> Object
>> Object.superclass
=> BasicObject
>> BasicObject.superclass
=> nil
I hope this gives us all a little better understanding about objects in Ruby.
One option in cases where you feel you would like to have direct pointer operations is to use the replace method of Hashes, Arrays & Strings.
this is useful for when you would like to have a method return a variable that a proc the method sets up will change at a later date, and don't want the annoyance of using a wrapper object.
example:
def hash_that_will_change_later
params = {}
some_resource.on_change do
params.replace {i: 'got changed'}
end
params
end
a = hash_that_will_change_later
=> {}
some_resource.trigger_change!
a
{i: 'got changed'}
It's probably better generally to use explicit object wrappers for such cases, but this pattern is useful for building specs/tests of asynchronous stuff.
I'm no Ruby expert. But for a technically crazy kluge...that would only work if you felt like going through eval every time you worked with a variable:
>> a = 5
=> 5
>> b = :a
=> :a
>> eval "#{b} = 4"
=> 4
>> eval "#{a}"
=> 4
>> eval "#{b}"
=> 4
Note that a direct usage of b will still give you :a and you can't use it in expressions that aren't in eval:
>> b
=> :a
>> b + 1
NoMethodError: undefined method `+' for :a:Symbol
...and there are certainly a ton of caveats. Such as that you'd have to capture the binding and pass it around in more complex scenarios...
'pass parameter by reference' in Ruby?
#Paul.s has an answer for if you can change the point of declaration to be a wrapper object, but if you can only control the point of reference then here's a BasicReference class I tried:
class BasicReference
def initialize(r,b)
#r = r
#b = b
#val = eval "#{#r}", #b
end
def val=(rhs)
#val = eval "#{#r} = #{rhs}", #b
end
def val
#val
end
end
a = 5
puts "Before basic reference"
puts " the value of a is #{a}"
b = BasicReference.new(:a, binding)
b.val = 4
puts "After b.val = 4"
puts " the value of a is #{a}"
puts " the value of b.val is #{b.val}"
This outputs:
Before basic reference
the value of a is 5
After b.val = 4
the value of a is 4
the value of b.val is 4