Hin und wieder stoße ich auf die Vorstellung, dass R eine Semantik zum Kopieren und Ändern hat , zum Beispiel in Hadleys Devtools-Wiki .
Die meisten R-Objekte verfügen über eine Semantik zum Kopieren und Ändern, sodass durch Ändern eines Funktionsarguments der ursprüngliche Wert nicht geändert wird
Ich kann diesen Begriff auf die R-Help-Mailingliste zurückführen. Zum Beispiel schrieb Peter Dalgaard im Juli 2003 :
R ist eine funktionale Sprache mit verzögerter Auswertung und schwacher dynamischer Typisierung (eine Variable kann den Typ nach Belieben ändern: a <- 1; a <- "a" ist zulässig). Semantisch ist alles Copy-on-Modify, obwohl bei der Implementierung einige Optimierungstricks verwendet werden, um die schlimmsten Ineffizienzen zu vermeiden.
Ebenso schrieb Peter Dalgaard im Januar 2004 :
R verfügt über eine Semantik zum Kopieren und Ändern (im Prinzip und manchmal in der Praxis). Sobald sich ein Teil eines Objekts ändert, müssen Sie möglicherweise an neuen Stellen nach allem suchen, was es enthält, einschließlich möglicherweise des Objekts selbst.
Noch weiter zurück, im Februar 2000, sagte Ross Ihaka:
Wir haben viel Arbeit investiert, um dies zu erreichen. Ich würde die Semantik als "Kopie beim Ändern (falls erforderlich)" beschreiben. Das Kopieren erfolgt nur, wenn Objekte geändert werden. Der (falls erforderlich) Teil bedeutet, dass wir, wenn wir nachweisen können, dass die Änderung keine nicht lokalen Variablen ändern kann, einfach ohne Kopieren Änderungen vornehmen.
Es ist nicht im Handbuch
Egal wie intensiv ich gesucht habe, ich kann in den R-Handbüchern weder in der R-Sprachdefinition noch in den R -Interna einen Verweis auf "Copy-on-Modify" finden
Frage
Meine Frage besteht aus zwei Teilen:
- Wo ist das formal dokumentiert?
- Wie funktioniert Copy-on-Modify?
Ist es zum Beispiel richtig, von "Pass-by-Reference" zu sprechen, da ein Versprechen an die Funktion weitergegeben wird?
quelle
Antworten:
Call-by-Value
Die R-Sprachdefinition sagt dies aus (in Abschnitt 4.3.3 Argumentbewertung )
Dies beschreibt zwar nicht den Mechanismus, nach dem Copy-on-Modify funktioniert, erwähnt jedoch, dass das Ändern eines an eine Funktion übergebenen Objekts keine Auswirkungen auf das Original im aufrufenden Frame hat.
Weitere Informationen, insbesondere zum Aspekt des Kopierens und Änderns, finden Sie in der Beschreibung von
SEXP
s im Handbuch R Internals , Abschnitt 1.1.2 Rest des Headers . Insbesondere heißt es [Hervorhebung hinzugefügt]Dies beschreibt zwar nicht die Situation, in der Objekte als Argumente an Funktionen übergeben werden, wir können jedoch den Schluss ziehen, dass derselbe Prozess abläuft, insbesondere angesichts der Informationen aus der zuvor zitierten Definition der R-Sprache.
Versprechen bei der Funktionsbewertung
Ich glaube nicht , es ganz richtig ist zu sagen , dass ein Versprechen ist vergangen an die Funktion. Die Argumente werden an die Funktion übergeben und die tatsächlich verwendeten Ausdrücke werden als Versprechen gespeichert (plus einem Zeiger auf die aufrufende Umgebung). Nur wenn ein Argument ausgewertet wird, wird der in dem Versprechen gespeicherte Ausdruck in der durch den Zeiger angegebenen Umgebung abgerufen und ausgewertet. Dieser Vorgang wird als Forcen bezeichnet .
Insofern halte ich es nicht für richtig, diesbezüglich über Pass-by-Reference zu sprechen . R hat eine Call-by-Value- Semantik, versucht jedoch, das Kopieren zu vermeiden, es sei denn, ein an ein Argument übergebener Wert wird ausgewertet und geändert.
Der NAMED-Mechanismus ist eine Optimierung (wie von @hadley in den Kommentaren angegeben), mit der R verfolgen kann, ob bei Änderungen eine Kopie erstellt werden muss. Es gibt einige Feinheiten, die genau mit der Funktionsweise des NAMED-Mechanismus zusammenhängen, wie von Peter Dalgaard erörtert (im R Devel-Thread zitiert @mnel in seinem Kommentar zur Frage)
quelle
NAMED
Konzept nicht auch bei Funktionsaufrufen verwendet, mit der zusätzlichen Ausgabe von Versprechungen?Ich habe einige Experimente damit durchgeführt und festgestellt, dass R das Objekt bei der ersten Änderung immer kopiert.
Sie können das Ergebnis auf meinem Computer unter http://rpubs.com/wush978/5916 sehen
Bitte lassen Sie mich wissen, wenn ich einen Fehler gemacht habe, danke.
Um zu testen, ob ein Objekt kopiert wurde oder nicht
Ich speichere die Speicheradresse mit dem folgenden C-Code:
#define USE_RINTERNALS #include <R.h> #include <Rdefines.h> SEXP dump_address(SEXP src) { Rprintf("%16p %16p %d\n", &(src->u), INTEGER(src), INTEGER(src) - (int*)&(src->u)); return R_NilValue; }
Es werden 2 Adressen gedruckt:
SEXP
integer
Lassen Sie uns diese C-Funktion kompilieren und laden.
Rcpp:::SHLIB("dump_address.c") dyn.load("dump_address.so")
Sitzungsinfo
Hier ist die
sessionInfo
Testumgebung.Beim Schreiben kopieren
Zuerst teste ich die Eigenschaft von copy beim Schreiben , was bedeutet, dass R das Objekt nur kopiert, wenn es geändert wird.
a <- 1L b <- a invisible(.Call("dump_address", a)) invisible(.Call("dump_address", b)) b <- b + 1 invisible(.Call("dump_address", b))
Das Objekt
b
kopierta
bei der Änderung von. R implementiert diecopy on write
Eigenschaft.Ändern Sie den Vektor / die Matrix an Ort und Stelle
Dann teste ich, ob R das Objekt kopiert, wenn wir ein Element eines Vektors / einer Matrix ändern.
Vektor mit Länge 1
a <- 1L invisible(.Call("dump_address", a)) a <- 1L invisible(.Call("dump_address", a)) a[1] <- 1L invisible(.Call("dump_address", a)) a <- 2L invisible(.Call("dump_address", a))
Die Adresse ändert sich jedes Mal, was bedeutet, dass R den Speicher nicht wiederverwendet.
Langer Vektor
system.time(a <- rep(1L, 10^7)) invisible(.Call("dump_address", a)) system.time(a[1] <- 1L) invisible(.Call("dump_address", a)) system.time(a[1] <- 1L) invisible(.Call("dump_address", a)) system.time(a[1] <- 2L) invisible(.Call("dump_address", a))
Für lange Vektoren verwendet R den Speicher nach der ersten Änderung wieder.
Darüber hinaus zeigt das obige Beispiel auch, dass "an Ort und Stelle ändern" die Leistung beeinträchtigt, wenn das Objekt sehr groß ist.
Matrix
system.time(a <- matrix(0L, 3162, 3162)) invisible(.Call("dump_address", a)) system.time(a[1,1] <- 0L) invisible(.Call("dump_address", a)) system.time(a[1,1] <- 1L) invisible(.Call("dump_address", a)) system.time(a[1] <- 2L) invisible(.Call("dump_address", a)) system.time(a[1] <- 2L) invisible(.Call("dump_address", a))
Es scheint, dass R das Objekt nur bei den ersten Änderungen kopiert.
Ich weiß nicht warum.
Attribut ändern
system.time(a <- vector("integer", 10^2)) invisible(.Call("dump_address", a)) system.time(names(a) <- paste(1:(10^2))) invisible(.Call("dump_address", a)) system.time(names(a) <- paste(1:(10^2))) invisible(.Call("dump_address", a)) system.time(names(a) <- paste(1:(10^2) + 1)) invisible(.Call("dump_address", a))
Das Ergebnis ist das gleiche. R kopiert das Objekt nur bei der ersten Änderung.
quelle