Changing the value of ruby ​​variables / links

I just stumbled upon what I do not quite understand. I know that ruby ​​variables are references. So amazing stuff is possible. But when I pass the variable to the method, it behaves strangely:

my_var_a = "nothing happend to me" my_var_b = "nothing happend to me" def parse_set(my_var_set) my_var_set = "my value changed" end def parse_sub(my_var_sub) my_var_sub.sub! /(.*)/, "my value changed" end parse_set(my_var_a) parse_sub(my_var_b) my_var_a # => "nothing happend to me" my_var_b # => "my value changed" 

Can you explain to me why it works with sub! and = , leaves the object unchanged? How can I avoid using sub! but having the same result?

+4
source share
3 answers

my_var_a and my_var_set are different links, but they point to the same object. If you change the object in my_var_set , the change will appear in my_var_a . However, if you my_var_set on a new object, this does not change what my_var_a points to.

Edit: Explanation ...

What Ruby does is refer to passing values ​​by value. When you say

 my_var_a = "nothing happend to me" 

Ruby saves the line “nothing happened to me” in the memory cell (let it be called 1000) and saves the link my_var_a in another memory cell (say 2000). When your code uses my_var_a , the interpreter looks at location 2000, sees 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 link called my_var_set and points it to the line pointed to by my_var_a (memory 1000). However, my_var_set is a copy of the my_var_a link — let's say my_var_set was created in memory location 3000. my_var_a and my_var_set are two completely different links in memory, they just point to the exact exact memory location that the string value contains.

The my_var_set = "my value changed" parse_set in parse_set creates a new line in memory and points my_var_set in this new memory location. However, this does not change what the reference point my_var_a ! Now, when my_var_set points to another memory location, nothing you do with this variable will affect my_var_a .

The same copying of links occurs for parse_sub . But the reason parse_sub modifies the string is because you are calling the method directly from my_var_sub . When you do this, the interpreter gets the object that my_var_sub points my_var_sub , and then modifies it. Thus, the change will be displayed in the my_var_a directory, since it still points to the same line.

+11
source
 irb(main):008:0* a = 'abc' => "abc" irb(main):009:0> a.replace('def') => "def" irb(main):010:0> a => "def" 

I had to use replace exactly zero time for all the years that I have been programming in Ruby. I wonder if there is a better design for your code.

Most string methods that change the receiver are suffix! (sub !, capizeize !, chomp !, etc.) Some that change the receiver are not a suffix! (replace one). If you call a method that modifies the receiver, all references to this object will see the change. if you call a method that does not change the receiver, the method returns a new line.

gsub does not change the receiver, but instead returns a new instance of String:

 a = "foo" b = a p a.sub(/o/, '#') # "f##" pa # => "foo" pb # => "foo" 

GSUB! modifies the receiver:

 a = "foo" b = a p a.sub!(/o/, '#') # => "f#o" pa # => "f#o" pb # => "f#o" 
+5
source

"How can I avoid using sub !, but with the same result?"

 def parse_set() "my value changed" end a = parse_set() 

Set it as an instance variable of an object (although this only works in the main script, I recommend creating your own class if you want to use the instance variable approach).

 @my_var_a = "nothing happend to me" def parse_set() @my_var_a = "my value changed" end 
0
source

Source: https://habr.com/ru/post/1299657/


All Articles