Können Sie in R als Referenz übergeben?

68

Können Sie mit "R" verweisen? Zum Beispiel im folgenden Code:

setClass("MyClass",
    representation(
    name="character"
    ))


instance1 <-new("MyClass",name="Hello1")
instance2 <-new("MyClass",name="Hello2")

array = c(instance1,instance2)

instance1
array

instance1@name="World!"

instance1
array

die Ausgabe ist

> instance1
An object of class “MyClass”
Slot "name":
[1] "World!"

> array
[[1]]
An object of class “MyClass”
Slot "name":
[1] "Hello1"


[[2]]
An object of class “MyClass”
Slot "name":
[1] "Hello2"

aber ich wünschte es wäre

> instance1
An object of class “MyClass”
Slot "name":
[1] "World!"

> array
[[1]]
An object of class “MyClass”
Slot "name":
[1] "World!"


[[2]]
An object of class “MyClass”
Slot "name":
[1] "Hello2"

ist es möglich ?

Pierre
quelle
2
Ich frage mich wirklich, warum sie auf eine so ungewöhnliche Implementierung gekommen sind.
Anilbey
4
Objekte oder Grundelemente? S3, S4 oder R6? Verwenden Sie Umgebungen oder auf andere Weise? R 1.x, 2.x oder 3.x? Die Antworten hier erstrecken sich über den Zeitraum 2010-15 und stimmen nicht überein. Diese Frage ist ein unheiliges Durcheinander und muss aufgeräumt werden. Es ist auch nützlich, wenn Sie "Ja / Nein" sagen, um Veröffentlichungen oder Daten zu zitieren: z. B. "ab R 3.0 / 2013". Um die Antwort zukunftssicher zu machen.
smci

Antworten:

53

Nein .

Objekte in Zuweisungsanweisungen sind unveränderlich. R kopiert das Objekt und nicht nur die Referenz.

> v = matrix(1:12, nrow=4)
> v
           [,1] [,2] [,3]
     [1,]    1    5    9
     [2,]    2    6   10
     [3,]    3    7   11
     [4,]    4    8   12
> v1 = v
> v1[,1]     # fetch the first column 
     [1] 1 2 3 4

( Vorbehalt : Die obige Aussage gilt für R- Primitive , z. B. Vektoren, Matrizen) und auch für Funktionen ; Ich kann nicht mit Sicherheit sagen, ob dies für alle R-Objekte gilt - nur für die meisten von ihnen sowie für die überwiegende Mehrheit der am häufigsten verwendeten.)

Wenn Ihnen dieses Verhalten nicht gefällt, können Sie es mithilfe eines R-Pakets deaktivieren. Zum Beispiel gibt es ein R-Paket namens R.oo , mit dem Sie das Pass-by-Reference-Verhalten nachahmen können. R.oo ist auf CRAN verfügbar .

Doug
quelle
5
Siehe auch die mutatrund protoPakete.
Hadley
mutatrscheint nicht unterstützt und nicht dokumentiert.
krlmlr
@doug können Sie als Referenz mit einem .CallWrapper eines C ++ übergeben?
Nopeva
12
Ich finde dieses "Nein". eher fett , da viele Pakete den Durchgang als Referenz sowie die Rcpp Schnittstelle zu C / C ++ zu ermöglichen scheinen .
Hugo Raguet
44

Beachten Sie, dass R dies automatisch tut, wenn Sie die Referenzübergabe verwenden möchten, um die Auswirkungen des Kopierens eines Objekts, das nicht geändert wurde (wie es in anderen Sprachen mit konstanten Referenzen üblich ist), auf die Leistung zu vermeiden:

n <- 10^7
bigdf <- data.frame( x=runif(n), y=rnorm(n), z=rt(n,5) )
myfunc <- function(dat) invisible(with( dat, x^2+mean(y)+sqrt(exp(z)) ))
myfunc2 <- function(dat) {
    x <- with( dat, x^2+mean(y)+sqrt(exp(z)) )
    invisible(x)
}
myfunc3 <- function(dat) {
    dat[1,1] <- 0
    invisible( with( dat, x^2+mean(y)+sqrt(exp(z)) ) )
}
tracemem(bigdf)
> myfunc(bigdf)
> # nothing copied
> myfunc2(bigdf)
> # nothing copied!
> myfunc3(bigdf)
tracemem[0x6e430228 -> 0x6b75fca0]: myfunc3 
tracemem[0x6b75fca0 -> 0x6e4306f0]: [<-.data.frame [<- myfunc3 
tracemem[0x6e4306f0 -> 0x6e4304f8]: [<-.data.frame [<- myfunc3 
> 
> library(microbenchmark)
> microbenchmark(myfunc(bigdf), myfunc2(bigdf), myfunc3(bigdf), times=5)
Unit: milliseconds
            expr       min        lq    median        uq       max
1 myfunc2(bigdf)  617.8176  641.7673  644.3764  683.6099  698.1078
2 myfunc3(bigdf) 1052.1128 1134.0822 1196.2832 1202.5492 1206.5925
3  myfunc(bigdf)  598.9407  622.9457  627.9598  642.2727  654.8786
Ari B. Friedman
quelle
6
Sehr hilfreich zu wissen! Ich möchte auch hinzufügen, dass dies NUR für data.frames gilt. Matrizen / Arrays sind immer Wertübergabe, wie ich gerade nach ein paar Stunden Scratchen an der Rprof-Ausgabe erfahren habe.
Andrew Christianson
1
Führen Sie dies jetzt auf meinem Laptop erneut aus: Alle Zeiten sind jetzt gleich (und die Hälfte der vor fünf Jahren)
user189035
Der Tracemem-Teil muss ein wenig erklärt werden
Cloudscomputes
@AndrewChristianson IIUC, das ist das aktuelle Verhalten, auch matrix, arrayund tibble. Hat sich das Verhalten geändert, seit Sie Ihren Kommentar gepostet haben, oder irre ich mich?
Oren Milman
@OrenMilman oh mein Gott, wahrscheinlich? Dieser Kommentar stammt aus der Zeit vor einigen Jahren und wurde wahrscheinlich in Bezug auf R 2.15 / 2.14 gemacht, den ich damals verwendet habe.
Andrew Christianson
25

Wie bereits erwähnt, kann dies über Klassenobjekte erfolgen environment. Es gibt einen formalen Ansatz, der auf der Verwendung von environments aufbaut. Es heißt Referenzklassen und macht es Ihnen wirklich einfach. Suchen Sie ?setRefClassnach der Hilfeseite für den Haupteintrag. Außerdem wird beschrieben, wie formale Methoden mit Referenzklassen verwendet werden.

Beispiel

setRefClass("MyClass",
    fields=list(
        name="character"
    )
)

instance1 <- new("MyClass",name="Hello1")
instance2 <- new("MyClass",name="Hello2")

array = c(instance1,instance2)

instance1$name <- "World!"

Ausgabe

> instance1
Reference class object of class "MyClass"
Field "name":
[1] "World!"

> array
[[1]]
Reference class object of class "MyClass"
Field "name":
[1] "World!"

[[2]]
Reference class object of class "MyClass"
Field "name":
[1] "Hello2"
Rappster
quelle
19

Pass-by-Reference ist für environments möglich . Um sie zu verwenden, müssten Sie grundsätzlich jedes Mal, wenn Sie ein Objekt erstellen, auch einen Umgebungssteckplatz erstellen. Aber ich denke, dass es umständlich ist. Schauen Sie sich Pass by Reference für S4 an. und Zeiger und Verweis in R.

teucer
quelle
Die Links funktionieren jetzt. In diesen Tagen sollte der S4 einen vor S3 auflisten.
smci
6

R verfügt jetzt über eine Bibliothek, mit der Sie OOP mithilfe von Referenzen ausführen können. Siehe ReferenceClasses, das Teil des Methodenpakets ist.

Kyle Brandt
quelle
4

Tatsächlich emuliert das R.oo- Paket das Pass-by-Reference-Verhalten mithilfe von Umgebungen.


quelle
3

Wie bereits erwähnt, ist dies für S4-Klassen nicht möglich. Aber R bietet jetzt die Möglichkeit mit der R6- Bibliothek, die als Referenzklassen bezeichnet wird . Siehe offizielle Dokumentation

Jules Sam. Randolph
quelle
1
R6 ist ein vom Benutzer bereitgestelltes Paket. Referenzklassen sind etwas, das mit R (oder besser gesagt seinem Methodenpaket) geliefert wird. R6 ähnelt Referenzklassen, wie in der Dokumentation angegeben: "R6-Klassen ähneln den Standardreferenzklassen von R."
Helix123
2

Zusätzlich zu den anderen Antworten hier, die Ihr Objekt tatsächlich als Referenz übergeben ( environmentObjekte und Referenzklassen), sind Sie, wenn Sie aus syntaktischen Gründen nur an Call-by-Reference interessiert sind (dh es macht Ihnen nichts aus, wenn Ihre Daten darin kopiert werden), Sie könnte dies emulieren, indem der endgültige Wert bei der Rückgabe der externen Variablen zugewiesen wird:

byRef <- function(..., envir=parent.frame(), inherits=TRUE) {
  cl <- match.call(expand.dots = TRUE)
  cl[c(1, match(c("envir", "inherits"), names(cl), 0L))] <- NULL
  for (x in as.list(cl)) {
    s <- substitute(x)
    sx <- do.call(substitute, list(s), envir=envir)
    dx <- deparse(sx)
    expr <- substitute(assign(dx, s, envir=parent.frame(), inherits=inherits))
    do.call(on.exit, list(expr, add=TRUE), envir=envir)
  }
}

Dann können wir "Call-by-Reference" -Argumente deklarieren:

f <- function(z1, z2, z3) {
  byRef(z1, z3)

  z1 <- z1 + 1
  z2 <- z2 + 2
  z3 <- z3 + 3

  c(z1, z2, z3)
}

x1 <- 10
x2 <- 20
x3 <- 30

# Values inside:
print(f(x1, x2, x3))
# [1] 11 22 33

# Values outside:
print(c(x1, x2, x3))
# [1] 11 20 33

Beachten Sie, dass Sie die noch nicht geänderten Werte von außen erhalten , wenn Sie auf die "by-reference" -Variablen mit ihren externen Namen ( x1, x3) an einer beliebigen Stelle innerhalb der Funktion zugreifen . Außerdem behandelt diese Implementierung nur einfache Variablennamen als Argumente, sodass indizierte Argumente wie f(x[1], ...)diese nicht funktionieren (obwohl Sie dies wahrscheinlich mit einer etwas aufwendigeren Ausdrucksmanipulation implementieren könnten, um die Begrenzung zu umgehen assign).

Codeola
quelle
2

Zusätzlich zu den anderen Vorschlägen können Sie auch C / C ++ - Funktionen schreiben, die ihre Argumente als Referenz verwenden und an Ort und Stelle arbeiten , und sie dank Rcpp(unter anderem) direkt in R aufrufen . Siehe insbesondere diese Antwort .

Hugo Raguet
quelle