There's some way to reuse string? - ruby

In case,
A = "A"
B = "#{A}"
It's B = "A", right?
And now I would like to change (A = "C") and want B to change by effect of A too.
Is there some way to do that?

Let's talk about naming conventions first. Uppercase identifiers are used for constants in Ruby. Per default assigning a new value to an already initialized constant raises a warning in Ruby:
A = 'B'
A = 'C'
#=> warning: already initialized constant A
#=> warning: previous definition of A was here
Therefore I will use normal instance variables and reader methods in the following example. As Pascal Betz already pointed out: If you want b to depend on the current value of a then b should be a method:
def b
#a
end
#a = 'A'
b
#=> "A"
#a = 'C'
b
#=> "C"

If you do this:
a = "A"
b = "#{a}"
a and b are strings with the same content, but they're not the same objects:
b == a
# => true
b.equal? a
# => false
a.object_id
# => 24494240
b.object_id
# => 24679880
Ruby strings are mutable. So if b and a refer to the same string, modifying a will automatically update b too:
a = "A"
# => "A"
b = a
# => "A"
a.replace 'C'
# => "C"
b
# => "C"
It works in both directions:
b.gsub!('C', 'D')
# => "D"
a
# => "D"

Related

ruby : can't understand memory address

when I study clone vs dup, I tried copy a object like below :
a = {'key1' => 1, 'key2' => {'key3' => 3, 'key4' => 4}}.freeze
b = a.clone
c = a.dup
a['key1'] = 32
# => RuntimeError: can't modify frozen Hash
b['key1'] = 32
# => RuntimeError: can't modify frozen Hash
c['key1'] = 32
# => 32
upper case result that, a and b value are share the memory
but, when I check variable object_id, it returns different result :
a.object_id ## original
=> 70219554622880
b.object_id ## clone
=> 70219554589820
but in hash key object_id is same.
a['key1'].object_id ## original
=> 3
b['key1'].object_id ## clone
=> 3
c['key1'].object_id ## dup
=> 65
I don't understand a and b variable dosen't match object_id, but in hash key object_id is same.
Can you give me your thoughts, answers, or input?
thanks.
You might be confused specifically because you're using integers. Integers in Ruby are singletons: their integer value determines their object id.
x = 3
y = 3
x.object_id == y.object_id # true
x = "a"
y = "a"
x.object_id == y.object_id # false
However this is only true for integers up to a certain size.
x = 11111111111111111111111111111111
y = 11111111111111111111111111111111
x.object_id == y.object_id # false
This is because Ruby actually stores the integer value in the object id if it is small enough. If the number is too big it has to allocate a new object.

Reassigned hash changes the original hash

Why is variable a getting changed and how do I prevent it?
a = [] # => []
b = a # => []
b << :hello # => [:hello]
p a # => [:hello]
# >> [:hello]
I see the responds to use clone, and wondering why the below works and in which situations .clone is needed and not needed
a = "string" # => "string"
b =a # => "string"
b = "changed" # => "changed"
a # => "string"
Why is variable a getting changed and how do I prevent it?
a = [] # => []
b = a # => []
b << :hello # => [:hello]
p a # => [:hello]
# >> [:hello]
The variable a is not getting changed. The only way a variable can be changed is by assigning to it (ignoring reflection like Binding#local_variable_set), which you are not doing. Therefore, a doesn't change.
The object that is referenced by both a and b gets changed. But changing the object and changing the variable are two completely different things.
I see the responds to use clone, and wondering why the below works and in which situations .clone is needed and not needed
a = "string" # => "string"
b =a # => "string"
b = "changed" # => "changed"
a # => "string"
This works because you never change the object. You change the variable.
Why do you use a mutating method for the array and rebinding for the string and still expect they to behave similarly?
a = "string" #⇒ "string"
b = a #⇒ "string"
b << "changed" #⇒ "stringchanged"
a #⇒ "stringchanged"
As I understand its because of the memory usage.
When you initialize object, Ruby will first initialize object in the memory. Then, the variable points to that memory address. When you assign this variable to another one, that one will point to that address also
For example,
a = []
a.object_id # 70220203482480
b = a
b.object_id # 70220203482480
When you add new element, it means, you add the value to the array which initialized in memory, calling a and b will both show that array with new element.
a.push(1)
b # [1]
Let see the second example
c = 'reference'
d = c
c.object_id #70220203442960
d.object_id #70220203442960
c.capitalize! # 'Reference'
d # 'Reference'
If you assign d = 'new object', Ruby will create another object in the memory and give it value as string new object, and then, d will point to that new memory address
d = 'new object'
d.object_id # 70220203334840 (different one)
c # 'Reference' (cause c still point to the last object in memory)

The confusing Ruby method returns value

I have Ruby code:
def test_111(hash)
n = nil
3.times do |c|
if n
n[c] = c
else
n = hash
end
end
end
a = {}
test_111(a)
p a
Why it print {1=>1, 2=>2}, not the {} ??
In the test_111 method, the hash and the a use the same memory?
How can the a value be changed in the test_111 method?
I can't understand
Hashes are passed by reference. So, when you change a method parameter (which is a Hash), you change the original hash.
To avoid this, you should clone the hash.
test_111(a.dup)
This will create a shallow copy (that is, it will not clone child hashes that you may have).
A little illustration of what shallow copy is:
def mutate hash
hash[:new] = 1
hash[:existing][:value] = 2
hash
end
h = {existing: {value: 1}}
mutate h # => {:existing=>{:value=>2}, :new=>1}
# new member added, existing member changed
h # => {:existing=>{:value=>2}, :new=>1}
h = {existing: {value: 1}}
mutate h.dup # => {:existing=>{:value=>2}, :new=>1}
# existing member changed, no new members
h # => {:existing=>{:value=>2}}
In ruby, just about every object is passed by reference. This means when you do something as simple as
a = b
unless a was one of the simple types, after this assignment a and b will point to the same thing.
This means if you alter the second variable, the first is affected the same way:
irb(main):001:0> x = "a string"
=> "a string"
irb(main):002:0> y = x
=> "a string"
irb(main):003:0> x[1,0] = "nother"
=> "nother"
irb(main):004:0> x
=> "another string"
irb(main):005:0> y
=> "another string"
irb(main):006:0>
and of course the same applies for hashes:
irb(main):006:0> a = { :a => 1 }
=> {:a=>1}
irb(main):007:0> b = a
=> {:a=>1}
irb(main):008:0> a[:b] = 2
=> 2
irb(main):009:0> a
=> {:a=>1, :b=>2}
irb(main):010:0> b
=> {:a=>1, :b=>2}
irb(main):011:0>
If you don't want this to happen, use .dup or .clone:
irb(main):001:0> a = "a string"
=> "a string"
irb(main):002:0> b = a.dup
=> "a string"
irb(main):003:0> a[1,0] = "nother"
=> "nother"
irb(main):004:0> a
=> "another string"
irb(main):005:0> b
=> "a string"
irb(main):006:0>
For most people dup and clone have the same effect.
So if you write a function that modifies one of its parameters, unless you specifically want those changes to be seen by the code that calls the function, you should first dup the parameter being modified:
def test_111(hash)
hash = hash.dup
# etc
end
The behavior of your code is called a side effect - a change to the program's state that isn't a core part of the function. Side effects are generally to be avoided.

Crazy behavior (at least to me) when setting one string to another in Ruby

As a novice it's probably something about Ruby I missed, but for the life of me I don't understand this result. So I have this simple function:
def crazyfunc(s)
s.gsub!('a', 'b')
#return has not purpose here
end
Now I have this simple few sets.
s1 = 'abc'
s2 = s1
s2 = crazyfunc(str2)
puts s1
=> bbc
Why in the world is s1 affected by crazyfunc? So doing this instead:
def crazyfunc(s)
return s.gsub('a', 'b')
end
doesn't change str1, so I figure it's got to do with what the inplace gsub is doing. But I still don't get the logic of why str1 would be changed.
String assignment in Ruby doesn't implicitly copy the string. You are simply assigning another reference to it. If you want to copy the string, use clone.
To demonstrate, you can check object IDs:
ree-1.8.7-2010.02 > a = "foo"
=> "foo"
ree-1.8.7-2010.02 > b = a
=> "foo"
ree-1.8.7-2010.02 > a.object_id
=> 81728090
ree-1.8.7-2010.02 > b.object_id
=> 81728090
Since a and b have the same object ID, you know they're the same object. If you want to modify b as a copy of a, you can either use methods which return a new string (like gsub rather than gsub!), or you can use b = a.clone, and then operate on b.
ree-1.8.7-2010.02 > a = "foo"
=> "foo"
ree-1.8.7-2010.02 > b = a.clone
=> "foo"
ree-1.8.7-2010.02 > a.object_id
=> 81703030
ree-1.8.7-2010.02 > b.object_id
=> 81696040
ree-1.8.7-2010.02 > b.gsub! "f", "e"
=> "eoo"
ree-1.8.7-2010.02 > a
=> "foo"
ree-1.8.7-2010.02 > b
=> "eoo"
Or more simply:
ree-1.8.7-2010.02 > a = "foo"
=> "foo"
ree-1.8.7-2010.02 > b = a.gsub("f", "e")
=> "eoo"
ree-1.8.7-2010.02 > puts a, b
foo
eoo
You are running into the fact that everything in Ruby is an object, and that variables are just object references.
When you assign str1 = to str2, you are actually pointing them at the same object. When you then change the object pointed to by str2, you are also changing the object pointed to by str1.
In your original crazyfunc, you modify the string, return the modified string, and then change the value of the object pointed to by str2 - and str1, since they point to the same object.
s1 = 'abc'
s2 = s1
s2 = crazyfunc(str2)
A #master
B = A
C = B
A is A, and B equals A and C equals B. If you change B it doesn't affect A but affects C. See how it cascades down. The variables you list are just pointers.
So you only have 1 master variable here and you need to declare two.

Alias for array or hash element in Ruby

Example for array
arr = ["a", "b", "c"]
# TODO create an alias for arr[1] as x
x = "X"
# arr should be ["a", "X", "c"] here
Example for hash
hash = { :a => "aaa", :b => "bbb" , :c => "ccc" }
# TODO create an alias for hash[:b] as y
y = "YYY"
# hash should be { :a => "aaa", :b => "YYY" , :c => "ccc" } here
And also an alias for a variable?
var = 5
# TODO create an alias for var as z
z = 7
# var should be 7 here
Motivation: I have a big large deep construct of data, and you can imagine the rest. I want to use it in a read-only manner, but due to performance reasons copy is not permissible.
Metaphor: I want to choose context from a larger data structure and I want to access it with a short and simple name.
UPDATE: Problem solved as sepp2k advised. I just want to draw a summarizing picture here about the solution.
irb(main):001:0> arr = [ { "a" => 1, "b" => 2}, { "x" => 7, "y" => 8 } ]
=> [{"a"=>1, "b"=>2}, {"x"=>7, "y"=>8}]
irb(main):002:0> i = arr[0]
=> {"a"=>1, "b"=>2}
irb(main):004:0> j = arr[1]
=> {"x"=>7, "y"=>8}
irb(main):007:0> j["z"] = 9
=> 9
irb(main):008:0> j
=> {"x"=>7, "y"=>8, "z"=>9}
irb(main):009:0> arr
=> [{"a"=>1, "b"=>2}, {"x"=>7, "y"=>8, "z"=>9}]
What you want is not possible. There is no feature in ruby that you could use to make your examples work like you want.
However since you're saying you want to only use it read-only, there is no need for that. You can just do x = myNestedStructure[foo][bar][baz]. There will be no copying involved when you do that. Assignment does not copy the assigned object in ruby.
You would have to create a method that is your alias, which would update the data.
def y=(value)
arr[:b]=value
end
Then call it.
self.y="foo"
Edit: updated second code snippet.

Resources