我有一个由变量组成的数组,我想对每个变量执行相同的操作,并将结果存储在原始变量中:
(one, two, three) = [1, 2, 3]
[one, two, three].map!{|e| e += 1}
# => [2, 3, 4]
# But:
[one, two, three]
# => [1, 2, 3]
# You have to:
(one, two, three) = [one, two, three].map{|e| e += 1}
# => [2, 3, 4]
[one, two, three]
# => [2, 3, 4]
这似乎不是“正确的方式”,但我没有找到“正确的方法”。关于发生了什么,我也有一些模糊的想法,但我不太确定,所以我将不胜感激。
我的实际用例是我有命名参数,我是e = File.new(e) if e.is_a? String
答案 0 :(得分:4)
Ruby中的数字(例如Fixnum
)是不可变的。您无法更改基础值。
分配one = 1
后,如果没有新的分配,则无法更改one
的值。当你one += 1
时。您实际上是将新值2
分配给变量one
;这是一个全新的对象。
通过查看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
现在,在Array#map!
语句中,您实际上并没有更改one
对象。对该对象的引用存储在数组中;不是实际变量。当您使用map!
进行枚举时,块返回的对象将存储在相同位置的内部参考位置。想想map!
的第一次传递类似于以下内容:
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
由于这些Fixnum
对象是不可变的,因此无法更改其值。这就是您必须将map
的结果取消引用回原始值的原因:
(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
答案 1 :(得分:2)
试试这个:
a = [one, two, three]
a.map!{|e| e += 1}
问题是[one, two, three]
不是存储数组的变量,每次编写时都是一个全新的数组。设置a = [one, two, three]
后,您将拥有一个存储该值的变量,然后您可以对其进行操作。
Darshan在评论中指出,这实际上并没有修改原始变量1,2和3的值,而且他是正确的。但有一种方法可以做到:
["one", "two", "three"].each{ |e| eval "#{e} += 1" }
但这非常难看,依赖于在数组中使用字符串而不是实际的变量,并且可能比你已经提出的更糟糕了:
(one, two, three) = [one, two, three].map{|e| e += 1}
答案 2 :(得分:0)
如果你真的想要改变引用fixnums的变量的值,那么你所做的就是你在Ruby中可以做的最好的事情。也就是说,你可能会更好地不将它们存储为三个独立的变量。您可以拥有one
到two
并传递three
或a[0]
,而不是a[2]
,a
和h[:one]
。 }通过h[:three]
并传递h
。
a = [1, 2, 3]
a.map!{|e| e += 1}
a # => [2, 3, 4]
h = {:one=>1, :two=>2, :three=>3}
h.each_key{|k| h[k] += 1}
h # => {:one=>2, :two=>3, :three=>4}
使用Hash的第二个选项可能更接近你想要的,因为h[:some_name]
更接近使用变量名。