2014-10-09 91 views
2

我是Ruby新手,並且無法理解此方法中發生了什麼。Ruby - 方法更改輸入變量值

我做在Rails控制器這一呼籲 -

@arr = SomeClass.find_max_option(params[:x], @pos, params[:y], some_var) 

我試圖返回值@arr,成功地發生了,不過我的操作做出@pos該方法中被帶回以及; @pos的值在我僅嘗試獲取@arr的值時發生了變化。

下面是關於該方法

#before going into the method 
@pos = [a,b] 

def self.find_max_option(x, pos, y, some_var) 

pos.collect! { |element| 
    (element == b) ? [c,d] : element 
    } 
    end 

#new value of pos = [a, [c,d]] which is fine for inside in this method 

... #some calculations not relevant to this question, but pos gets used to generate some_array 

return some_array 

但是,當結束該方法,並得到回控制器詳情的@pos的值現在爲[A,[C,D]]爲好。

這是怎麼回事?我認爲pos將與@pos分開處理,並且價值不會帶回。作爲一種變通方法我剛纔創建的方法中的一個新的局部變量,但我想知道這是發生

#my workaround is to not modify the pos variable 
pos_groomed = pos.collect { |element| 
    (element == b) ? [c,d] : element 
    } 
    end 
+0

您正在傳遞'@ pos'作爲參數,這意味着'pos'是對'@ pos'的引用。你可以在'collect!'之前設置'pos = pos.dup'或者只使用非破壞性的'collect'。你的代碼並不是直截了當,因爲你正在返回一個不存在的變量 – engineersmnky 2014-10-09 18:18:01

+0

對於noob問題,我很抱歉,但這是所有Ruby方法的標準嗎?對方法中傳遞的參數進行更改傳播?所有參數默認都是輸入/輸出?我不記得這發生在Java中。 – 2014-10-09 18:20:53

+0

不要抱歉,這不是一個簡單的問題,即使是這樣,你也不應該感到抱歉。檢查這個http://stackoverflow.com/questions/1872110/is-ruby-pass-by-reference-or-by-value它會幫助你與這個話題。 – Nobita 2014-10-09 18:26:27

回答

3

而不是使用collect!,只需用collect(不!)。所以,重寫你的方法爲:

def self.find_max_option(x, pos, y, some_var) 
    pos.collect { |element| 
    (element == b) ? [c,d] : element 
    } 
end 

當使用!版本的collect,要更換由塊返回的值的每個元素。但是,在使用collect而不使用!時,會創建一個新數組,並且正在調用collect的對象不會被更改。請參閱該文檔:

collect! VS collect

在方法名末尾使用!是在Ruby中一種常見的做法。 This question是相關的,值得一看。

+0

我仍然需要在方法中稍後使用'pos''(我沒有顯示整個東西,只是相關的部分),所以要使用pos.collect我需要聲明一個新的變量像它一樣我在我的解決方法,對不對?問題更多 - 爲什麼「@ pos」也被改變了。 – 2014-10-09 18:22:42

+0

作爲這個答案的註釋,紅寶石約定是任何方法與!將會有「意想不到的副作用」。最經常的一個!會改變調用它的對象,但有時它會有其他副作用,如退出!退出時無需調用任何退出處理程序。當使用!方法,請始終查閱文檔並確保您瞭解「其他」發生的情況。 – 2014-10-09 18:23:32

+0

您需要執行dup,因爲@engineersmnky在評論中建議將該收集分配給方法中的另一個變量(就像您對pos_groomed所做的那樣),然後使用該變量。 – Nobita 2014-10-09 18:24:16

1

您正在使用collect的破壞性版本。 破壞性方法更改調用方法的對象,而非破壞性方法返回新對象。

Ruby開發人員傾向於將這些方法稱爲「爆炸方法」,因爲慣例是破壞性方法具有!後綴。

pos.collect! # changes pos and returns pos 
pos.collect # creates a new object 

您的解決方法只適用於您使用非破壞性收集,而原始代碼使用收集!

pos.collect do |element| 
(element == b) ? [c,d] : element 
end 

應該工作得很好。

至於爲何方法外的對象的變化:

在紅寶石,當將參數傳遞到方法時,實際上是通過基準的對象。 因此,將數組傳遞給方法不會創建副本,而只是將引用傳遞給原始數組。 沒有辦法'按價值傳遞',但是如果你真的必須的話,你可以用dup或clone創建一個副本。

+0

我會小心地說'你正在傳遞對該對象的引用'。在Ruby中這不是真的。 Ruby嚴格按照價值傳遞。 – Nobita 2014-10-09 19:15:17

+0

是的,你是對的,它的價值傳遞,但價值是一個對象的引用。這有點棘手 – 2014-10-09 20:51:24

+0

我這樣看:'變量是對象的引用'。 – Nobita 2014-10-09 20:58:15