2013-03-19 34 views
4

我只是在R中弄溼了自己的腳,並且很驚訝地看到函數不會修改對象,至少它看起來就是默認值。例如,我寫了一個函數,只是在表格中的一個標籤上粘上一個星號;它在函數內部工作,但是表本身沒有改變。 (我主要來自Ruby)在R中通過引用調用(使用函數修改對象)

那麼,什麼是使用函數來改變R中的對象的正常的,可接受的方式?我將如何添加星號到表標題?

  • 更換整個對象:myTable = title.asterisk(myTable)

  • 使用一個變通通過引用調用(如通過TszKin儒略描述於,例如,在Call by reference in R

  • 使用一些結構以外一個函數?一個對象方法?

+1

什麼對象類型是myTable? – 2013-03-19 11:16:58

+0

詳細說明:「table title」是myTable對象的一個​​屬性,如果有的話,你使用哪個函數來首先設置屬性? – 2013-03-19 11:23:52

+0

嗯,我對通用答案更感興趣,儘管我目前正在修改的實際表格是來自descr軟件包的CrossTable。標籤在創建對象時設置,我在說「attr(tbl $ t,'dimnames')[[2]] [level.index] = paste0(level.val,marker)」以添加星號到其中之一。 – 2013-03-19 12:09:53

回答

8

這兩個範例正在取代整個對象,如您指出,或wr iting '替代' 的功能,例如

`updt<-` <- function(x, ..., value) { 
    ## x is the object to be manipulated, value the object to be assigned 
    x$lbl <- paste0(x$lbl, value) 
    x 
} 

> d <- data.frame(x=1:5, lbl=letters[1:5]) 
> d 
    x lbl 
1 1 a 
2 2 b 
3 3 c 
> updt(d) <- "*" 
> d 
    x lbl 
1 1 a* 
2 2 b* 
3 3 c* 

這是的行爲,例如,$<- - 就地更新由$訪問的元素。 Here是一個相關的問題。人們可以想到的替換功能語法糖

updt1 <- function(x, ..., value) { 
    x$lbl <- paste0(x$lbl, value) 
    x 
} 
d <- updt1(d, value="*") 

但標籤「語法糖」並沒有真正做到公正,在我的腦海,到所涉及的中央範例。它支持方便的就地更新,這與R通常保持的變更時複製錯覺不同,它實際上是更新對象的'R'方式(而不是使用例如?ReferenceClasses,它具有更多其他語言的感覺,但會讓R用戶期待更改時語義的語義)。

+0

感謝您的快速回答。如果我從相關的問題中正確理解,「什麼是R中的替換函數?」,它們只是x <-f(x)的語法糖。是對的嗎? – 2013-03-19 12:00:16

17

您遇到問題的原因是您將對象傳遞給函數的本地名稱空間。這是關於R的偉大/可怕的事情之一:它允許隱式變量聲明,然後在名稱空間變得更深時實現超越。

這會影響你,因爲函數在當前名稱空間內創建一個新的名稱空間。我假設對象'myTable'最初是在全局命名空間中創建的,但是當它傳遞到函數'title.asterisk'時,新的函數 - 本地命名空間現在具有一個具有相同屬性的對象。這是這樣工作的:

title.asterisk <- function(myTable){ do some stuff to 'myTable' } 

在這種情況下,函數'title.asterisk'不會對全局對象'myTable'進行任何更改。而是使用相同的名稱創建本地對象,以便本地對象取代全局對象。如果我們以這種方式調用函數title.asterisk(myTable),則該函數僅對局部變量進行更改。

有兩種直接的方法來修改全局對象(以及許多間接方法)。

選項1:第一,你提到,是有函數返回的對象和覆蓋全球的對象,像這樣:

title.asterisk <- function(myTable){ 
    do some stuff to 'myTable' 
    return(myTable) 
} 
myTable <- title.asterisk(myTable) 

這是正常的,但你仍然讓你代碼有點難以理解,因爲實際上有兩個不同的'myTable'對象,一個是全局的,一個是本地的。很多編碼者通過添加一段時間來解決這個問題。在可變參數前面,像這樣:

title.asterisk <- function(.myTable){ 
    do some stuff to '.myTable' 
    return(.myTable) 
} 
myTable <- title.asterisk(myTable) 

好吧,現在我們有一個視覺提示,兩個變量是不同的。這很好,因爲當我們稍後嘗試調試我們的代碼時,我們不希望依賴像命名空間超類這樣的不可見事物。這隻會讓事情變得比他們要難。

選項2:您可以修改該函數中的對象。當你想對一個對象進行破壞性的編輯並且不希望內存膨脹時,這是更好的選擇。如果您正在進行破壞性編輯,則無需保存原始副本。另外,如果你的對象適當大,你不想在你不需要時拷貝它。要編輯全局命名空間對象,不要將它傳入或從函數內部聲明它。

title.asterisk <- function(){ do some stuff to 'myTable' } 

現在我們從函數內進行直接編輯對象「爲myTable」。我們沒有傳遞對象的事實使得我們的函數查看更高級別的名稱空間來嘗試和解析變量名稱。瞧,它發現了一個'myTable'對象更高!函數中的代碼將對對象進行更改。

需要考慮的注意事項:我討厭調試。我的意思是我真的討厭調試。這意味着我在R中有幾件事:

  • 我把幾乎所有東西都包裝在一個函數中。當我編寫代碼時,只要我得到一塊工作,我將其包裝在一個函數中,並將其放在一邊。我大量使用'。'前綴爲我的所有函數參數,並且對於它存在的名稱空間的任何本機名稱,不使用任何前綴。
  • 我嘗試不從函數內部修改全局對象。我不喜歡這導致。如果一個對象需要修改,我從聲明它的函數中修改它。這通常意味着我有調用函數的函數層,但它使我的工作既模塊化又易於理解。
  • 我評論了我的所有代碼,解釋每一行或每塊的意圖。這似乎有點不相關,但我發現這三樣東西一起爲我着想。一旦你開始在函數中包裝代碼,你會發現自己希望重用更多的舊代碼。這就是好評。對我來說,這是一個必要的部分。
+1

在帖子結尾明智的評論 - 絕對值得+1。 – 2013-03-19 13:22:16

+0

感謝您的好解釋。被Ruby嚴重灌輸,我很少考慮一個事實,即一個全局變量可以在一個函數中被修改,當我想到它時會發抖。但是不同的語言需要不同的技術,所以最好把它留給我們。 – 2013-03-19 13:28:54