2013-05-13 34 views
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

Ruby中的數字(例如Fixnum)是不可變的。您無法更改基礎值。

一旦分配了one = 1,就不可能在沒有新分配的情況下更改one的值。當你做one += 1。實際上,您將新值2分配給變量one;這是一個全新的對象。

你可以看到這個更清楚地看object_id(又名__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 
2

試試這個:

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

的問題是,[one, two, three]不是存儲的一個變量數組,每次寫入時都是一個全新的數組。一旦你設置了a = [one, two, three],你就有一個變量存儲你可以操作的值。


達山在評論中指出,這實際上並沒有修改原始變量一,二,三的價值觀,他是正確的。但是有一個辦法做到這一點:

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

但是,這是很醜陋,依靠陣列,而不是實際的變量使用字符串,可能比你已經想出了差了很多:

(one, two, three) = [one, two, three].map{|e| e += 1} 
+0

這不起作用。 「一」,「二」和「三」保持不變。 – 2013-05-13 21:42:32

+0

你是對的,Darshan。更新了我的答案。 – 2013-05-13 22:23:58

0

如果你真的想改變的變量是指fixnums的價值,那麼你在做什麼是最好你可以在Ruby中完成。也就是說,你可能會更好,而不是將它們存儲爲三個單獨的變量。而不是onetwo,並且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} 

第二個選項,使用哈希,可能是更接近你想要什麼,因爲h[:some_name]更接近使用變量名。