2

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

(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

4

3 回答 3

4

FixnumRuby 中的数字(例如)是不可变的。您无法更改基础价值。

一旦分配one = 1,就不可能在one没有新分配的情况下更改 的值。当你这样做one += 1。您实际上是在2为变量分配新值one;这是一个全新的对象。

通过查看object_id(aka __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
于 2013-05-13T21:50:44.657 回答
2

尝试这个:

a = [one, two, three]
a.map!{|e| e += 1}

问题[one, two, three]在于存储数组的不是变量,每次编写它都是一个全新的数组。一旦你设置a = [one, two, three]了,你就有一个变量来存储你可以操作的值。


Darshan 在评论中指出,这实际上并没有修改原始变量一、二和三的值,他是正确的。但是有一种方法可以做到:

["one", "two", "three"].each{ |e| eval "#{e} += 1" }

但这很丑陋,依赖于在数组中使用字符串而不是实际变量,并且可能比您已经想出的要糟糕得多:

(one, two, three) = [one, two, three].map{|e| e += 1}
于 2013-05-13T21:31:55.960 回答
0

如果您真的想更改引用 fixnums 的变量的值,那么您所做的就是您在 Ruby 中可以做的最好的事情。也就是说,最好不要将它们存储为三个单独的变量。除了具有onetwothree,您还可以具有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}

第二个选项,使用哈希,可能更接近你想要的,因为h[:some_name]更接近于使用变量名。

于 2013-05-13T21:52:43.853 回答