2010-04-17 13 views
12

ich oft im Wesentlichen Folgendes tun mag:kann lapply nicht Variablen in einem höheren Umfang ändern

mat <- matrix(0,nrow=10,ncol=1) 
lapply(1:10, function(i) { mat[i,] <- rnorm(1,mean=i)}) 

Aber ich, dass Matte 10 darin Zufallszahl hätte erwarten würde, sondern hat es 0. (Ich mache mir keine Sorgen um den RNorm-Part. Klar, es gibt einen richtigen Weg, das zu tun.Ich mache mir Sorgen über die Auswirkungen auf die Matte aus einer anonymen Funktion des Schlafens. Kann ich die Matrixmatte nicht von innen her beeinflussen? Warum nicht? Gibt es eine Scoping-Regel von R, die das blockiert?

Antwort

26

Ich habe dieses Problem in dieser verwandten Frage diskutiert: "Is R’s apply family more than syntactic sugar". Sie werden feststellen, dass, wenn Sie an der Funktion Signatur suchen for und apply sie einen entscheidenden Unterschied: a for Schleife einen Ausdruck auswertet, während eine apply Schleife eine auswertet Funktion.

Wenn Sie Dinge außerhalb des Anwendungsbereichs einer Anwendungsfunktion ändern möchten, müssen Sie <<- oder assign verwenden. Oder mehr auf den Punkt, verwenden Sie stattdessen etwas wie eine for Schleife. Aber Sie müssen wirklich vorsichtig sein, wenn Sie mit Dingen außerhalb einer Funktion arbeiten, da dies zu unerwartetem Verhalten führen kann.

Meiner Meinung nach ist einer der Hauptgründe, eine apply Funktion zu verwenden, explizit, weil es Dinge außerhalb davon nicht ändert. Dies ist ein Kernkonzept in der funktionalen Programmierung, wobei Funktionen side effects vermeiden. Dies ist auch ein Grund, warum die apply Familie von Funktionen in paralleler Verarbeitung verwendet werden kann (und ähnliche Funktionen existieren in den verschiedenen parallelen Paketen wie Schnee).

Schließlich ist der richtige Weg, um Ihr Codebeispiel auszuführen ist auch, wie dies in den Parameter an die Funktion übergeben, und die Ausgabe Zuordnung zurück:

mat <- matrix(0,nrow=10,ncol=1) 
mat <- matrix(lapply(1:10, function(i, mat) { mat[i,] <- rnorm(1,mean=i)}, mat=mat)) 

Es ist immer am besten über einen Parameter explizit zu sein wenn möglich (daher die mat=mat), anstatt daraus zu schließen.

1

Anstatt mat tatsächlich zu ändern, gibt lapply nur die geänderte Version von mat (als Liste) zurück. Sie müssen es nur der Matte zuweisen und mithilfe von as.matrix() in eine Matrix zurückverwandeln.

5

Einer der wichtigsten Vorteile von Funktionen höherer Ordnung wie lapply() oder sapply() ist, dass Sie müssen nicht Ihre „Container“ (Matrix in diesem Fall) initialisieren.

Wie Fojtasek schlägt vor:

as.matrix(lapply(1:10,function(i) rnorm(1,mean=i))) 

Alternativ:

do.call(rbind,lapply(1:10,function(i) rnorm(1,mean=i))) 

Oder einfach als numerischer Vektor:

sapply(1:10,function(i) rnorm(1,mean=i)) 

Wenn Sie wirklich wollen eine Variable oberhalb des ändern Umfang Ihrer anonymen Funktion (Zufallszahlengenerator in diesem Fall), verwenden Sie <<-

> mat <- matrix(0,nrow=10,ncol=1) 
> invisible(lapply(1:10, function(i) { mat[i,] <<- rnorm(1,mean=i)})) 
> mat 
      [,1] 
[1,] 1.6780866 
[2,] 0.8591515 
[3,] 2.2693493 
[4,] 2.6093988 
[5,] 6.6216346 
[6,] 5.3469690 
[7,] 7.3558518 
[8,] 8.3354715 
[9,] 9.5993111 
[10,] 7.7545249 

Siehe this post über <<-. Aber in diesem speziellen Beispiel eine for-Schleife würde einfach mehr Sinn machen:

mat <- matrix(0,nrow=10,ncol=1) 
for(i in 1:10) mat[i,] <- rnorm(1,mean=i) 

mit den Nebenkosten für eine Indexvariable zu schaffen, i, im globalen Arbeitsbereich.