How do I dynamically create a local variable in Ruby? - ruby

I am trying to dynamically create local variables in Ruby using eval and mutate the local-variables array. I am doing this in IRB.
eval "t = 2"
local_variables # => [:_]
eval "t"
# => NameError: undefined local variable or method `t' for main:Object
local_variables << "t".to_sym # => [:_, :t]
t
# => NameError: undefined local variable or method `t' for main:Object

You have to synchronize the evaluations with the same binding object. Otherwise, a single evaluation has its own scope.
b = binding
eval("t = 2", b)
eval("local_variables", b) #=> [:t, :b, :_]
eval("t", b) # => 2
b.eval('t') # => 2

You have to use the correct binding. In IRB for example this would work:
irb(main):001:0> eval "t=2", IRB.conf[:MAIN_CONTEXT].workspace.binding
=> 2
irb(main):002:0> local_variables
=> [:t, :_]
irb(main):003:0> eval "t"
=> 2
irb(main):004:0> t
=> 2

You could set instance variables like this:
instance_variable_set(:#a, 2)
#a
#=> 2

Related

What does "[]" represent in the following context?

Could someone please explain the difference between:
&.[](:key)
.try(:[],:key)
.try(:key)
Specially what the "[]" represents in the first and second?
.[] is a method on the Hash object.
x.[](:key) calls the [] method with the :key argument and is equivalent to x[:key]
& is the safe navigation operator. x&.[](:key) would return nil if x is nil while x.[](:key) would cause an error.
x = {key: 123}
x[:key]
# => 123
x.[](:key)
# => 123
x = nil
x.[](:key)
# NoMethodError: undefined method `[]' for nil:NilClass
x&.[](:key)
# => nil
As far as the differences go, I don't believe there are any between the first and second, however x.try(:key) would try to call a key method on x and that would error out because it doesn't exist.
x.try(:[], :key) calls the .[] method with :key as an argument and is equivalent to what we saw above. As with the safe navigation operator, try returns nil if x is nil.
For better understanding, [] is a special method similar to fetch on Hash:
hash = {a: 1}
hash.[](:a) # => 1
hash.fetch(:a) # => 1
try is from Rails, could be used with syntax Hash#try(:method, argument):
hash.try(:[], :a) # => 1
hash.try(:fetch, :a) # => 1

Why is ruby acting like passing by reference when using gsub function in Ruby? [duplicate]

This question already has answers here:
Ruby 'pass by value' clarification [duplicate]
(3 answers)
Closed 4 years ago.
Given the following two methods:
[53] pry(main)> def my_method
[53] pry(main)* leti = 'leti'
[53] pry(main)* edit(leti)
[53] pry(main)* leti
[53] pry(main)* end
=> :my_method
[54] pry(main)> def edit(a_leti)
[54] pry(main)* a_leti.gsub!('e', '3')
[54] pry(main)* a_leti
[54] pry(main)* end
=> :edit
[55] pry(main)> my_method
=> "l3ti"
Can someone explain why I am getting the value edited inside the edit method and not the original value ('leti'). I though Ruby was passed by value. In fact, if instead of using the function gsub I use a simple assignment, I get the original value. Does the gsub! make it by reference?
Thank you!
In Ruby: Objects like strings are passed by reference. Variables with objects like strings are in fact references to those strings. Parameters are passed by value. However, for strings, these are references to those strings.
So here is the classic example:
irb(main):004:0* a = "abcd"
=> "abcd"
irb(main):005:0> b = a
=> "abcd"
irb(main):006:0> b << "def"
=> "abcddef"
irb(main):007:0> a
=> "abcddef"
irb(main):008:0> b
=> "abcddef"
If you do not wish to modify the original string, you need to make a copy of it:
Three ways (of many) to do this are:
b = a.dup
b = a.clone
b = String.new a
Using dup
irb(main):009:0> a = "abcd"
=> "abcd"
irb(main):010:0> b = a.dup
=> "abcd"
irb(main):011:0> b << "def"
=> "abcddef"
irb(main):012:0> a
=> "abcd"
irb(main):013:0> b
=> "abcddef"
BTW: For myself, this effect is the number one cause of defects in my own code.

NoMethodError: undefined method `except' for Hash

While using except on Hash in Ruby,
d = {}
d["a"]=1234
d["b"]=34
d["c"]=3
d.except(:b,:c)
I am getting NoMethodError:
NoMethodError: undefined method `except' for {"a"=>1234, "b"=>34, "c"=>3}:Hash from (irb):6 from
/Users/niranjan/.rvm/rubies/ruby-1.9.3-p551/bin/irb:12:in `<main>'
What am I doing wrong?
except is a Rails method (ActiveSupport to be exact). Your code does not reproduce that error when executing in Rails console:
> d = {}
# => {}
> d["a"]=1234
# => 1234
> d["b"]=34
# => 34
> d["c"]=3
# => 3
> d.except(:b,:c)
# => {"a"=>1234, "b"=>34, "c"=>3}
There is no Hash#except. You can implement it as follows:
d.reject { |k, v| ["b", "c"].include? k }
# => {"a"=>1234}
Note that it is not a Hash with indifferent access; "b" is not the same thing as :b.
As they have said above, there is no method 'except' meaning you have not defined 'except' anywhere in this code. If you need a refresher on how to build Hashes, a good one is: http://www.tutorialspoint.com/ruby/ruby_hashes.htm
Hash#except will be in Ruby 3
h = { a: 1, b: 2, c: 3 }
p h.except(:a) #=> {:b=>2, :c=>3}
https://www.ruby-lang.org/en/news/2020/09/25/ruby-3-0-0-preview1-released/

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.

Behaviour of local_variables - can anybody explain?

just curious why I cann't remove declared local variable from 'local_variables' array.
Example:
x=1
myarr = local_variables.clone
p local_variables
=> [:x, :_]
p myarr
=> [:x, :_]
p local_variables.class
=> Array
p myarr.class
=> Array
myarr.delete :x
p myarr
=> [:_]
local_variables.delete :x
p local_variables
=> [:x, :_]
WTF ?
I did suspected calling local_variables.delete with parameter :x reinserts it back as it is declared anew. But if called with other previously undeclared symbol does not change it:
p local_variables
=> [:x, :_]
local_variables.delete :whatever
p local_variables
=> [:x, :_]
Can somebody explain ?
Thx.
local_variables returns an array containing the names of all currently declared local variables. You can do anything you want with that array, but this is obviously not going to affect which local variables are declared. Why would it? If you strike out a name from phone book, does that person die?
Consider the following method:
def foo
[42, 23, 13]
end
foo #=> [42, 23, 13]
foo.delete 23
foo #=> [42, 23, 13]
Does that behaviour surprise you?
Every time you call local_variables (or foo) a new array is created. If you modify that new array that has no effect on what will happen if you call local_variables again.
AFAIK it's not possible to remove variables:
http://programming.itags.org/ruby/64695/

Resources