更改数组中变量的值

时间:2013-05-13 21:25:36

标签: arrays ruby iteration

我有一个由变量组成的数组,我想对每个变量执行相同的操作,并将结果存储在原始变量中:

(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

3 个答案:

答案 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中可以做的最好的事情。也就是说,你可能会更好地将它们存储为三个独立的变量。您可以拥有onetwo并传递threea[0],而不是a[2]ah[: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]更接近使用变量名。