Puteți trece prin referință în R? (Programare, R, Oop, Trecerea Parametrilor, Trece Prin Referință)

Pierre a intrebat.

Se poate trece prin referință cu „R” ?de exemplu, în următorul cod:

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


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

array = c(instance1,instance2)

instance1
array

[email protected]="World!"

instance1
array

rezultatul este

> 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"

dar mi-aș dori să fie

> 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"

este posibil?

Comentarii

  • Chiar mă întreb de ce au venit cu o implementare atât de neobișnuită. –  > Por anilbey.
  • Obiecte sau primitive? S3, S4 sau R6? Folosind medii sau altfel? R 1.x, 2.x sau 3.x? Răspunsurile de aici se întind din 2010-15 și nu sunt în dezacord unul cu celălalt. Această întrebare este un dezastru și trebuie curățată. De asemenea, este util ca atunci când se răspunde „Da/Nu” să se citeze versiuni sau date: de exemplu, „începând cu R 3.0 / 2013”. Pentru ca răspunsul să fie pregătit pentru viitor. –  > Por smci.
9 răspunsuri
doug

Nu.

Obiectele din instrucțiunile de atribuire sunt imuabile. R va copia obiectul nu doar referința.

> 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

(rezervă: afirmația de mai sus este adevărată pentru R primitive(de exemplu, vectori, matrici), dar și pentru funcții; nu pot spune cu certitudine dacă este adevărată și pentru toate obiectele R – doar pentru cele mai multe dintre ele, precum și pentru marea majoritate a celor mai des folosite).

Dacă nu vă place acest comportament, puteți renunța la el cu ajutorul unui pachet R. De exemplu, există un pachet R numit R.oo care vă permite să imitați comportamentul pass-by-reference; R.oo este disponibil pe CRAN.

Comentarii

  • A se vedea și mutatr și proto pachete. –  > Por hadley.
  • mutatr pare a fi nesusținut și nedocumentat. –  > Por krlmlr.
  • @doug poți să treci prin referință folosind un .Call wrapper al unui C++? –  > Por nopeva.
  • Găsesc acest „Nu” mai degrabă, bine, bold, din moment ce multe pachete par să permită trecerea prin referință, precum și Rcpp interfața cu C/C++. –  > Por Hugo Raguet.
Ari B. Friedman

Rețineți că, dacă sperați să folosiți trecerea prin referință doar pentru a evita implicațiile de performanță ale copierii unui obiect care nu este modificat (așa cum se întâmplă frecvent în alte limbaje cu referințe constante), R face acest lucru în mod automat:

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

Comentarii

  • Foarte util de știut! Aș adăuga, de asemenea, că acest lucru pare să se aplice NUMAI la data.frames. Matricile/Array-urile sunt întotdeauna pass-by-value, după cum tocmai am aflat după câteva ore de scărpinat în ieșirea Rprof. –  > Por Andrew Christianson.
  • rulează din nou acest lucru pe laptopul meu acum: toate timpii sunt aceiași acum (și jumătate din cei de acum cinci ani) – –  > Por user189035.
  • Partea de tracemem trebuie să fie explicată un pic –  > Por cloudscomputes.
  • @AndrewChristianson IIUC, acesta este comportamentul actual, de asemenea pentru matrix, array și tibble. S-a schimbat comportamentul de când ai postat comentariul tău sau mă înșel? –  > Por Oren Milman.
  • @OrenMilman oh, Doamne, probabil? acest comentariu este de acum câțiva ani și probabil că a fost făcut cu referire la R 2.15 / 2.14, pe care îl foloseam la acea vreme. –  > Por Andrew Christianson.
Rappster

După cum au subliniat mai mulți înainte, acest lucru se poate face prin utilizarea obiectelor din clasa environment. Există o abordare formală care se bazează pe utilizarea clasei environments. Se numește Clase de referință și vă ușurează foarte mult lucrurile. Consultați ?setRefClass pentru pagina de ajutor pentru intrarea principală. Aceasta descrie, de asemenea, cum se utilizează metodele formale cu clasele de referință.

Exemplu

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

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

array = c(instance1,instance2)

instance1$name <- "World!"

Ieșire

> 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"

teucer

Pass-by-reference este posibil pentru environments. Pentru a le utiliza, practic, ori de câte ori creați un obiect, va trebui să creați și un slot de mediu. Dar cred că acest lucru este greoi. Aruncați o privire la Pass by reference pentru S4. și Pointeri și trecerea prin referință în R

Comentarii

  • Linkurile funcționează acum. În aceste zile ar trebui să apară S4 înaintea lui S3. –  > Por smci.
Kyle Brandt

R are acum o bibliotecă care vă permite să faceți OOP folosind referințe. A se vedea ReferenceClasses care face parte din pachetul de metode.

user350780

În realitate, aplicația R.oo emulează comportamentul pass-by-reference prin utilizarea mediilor.

Jules Sam. Randolph

Așa cum au afirmat și alții, nu este posibil pentru clasele S4. Dar R oferă acum această posibilitate cu R6 numită referință clase de referință. A se vedea documentația oficială

Comentarii

  • R6 este un pachet la care au contribuit utilizatorii. Clasele de referință sunt ceva ce vine cu R (sau mai degrabă cu pachetul de metode al acestuia). R6 este similar cu clasele de referință, după cum se spune în documentație: „Clasele R6 sunt similare cu clasele de referință standard ale R”. –  > Por Helix123.
codeola

În plus față de celelalte răspunsuri de aici care de fapt trec obiectul prin referință (environment obiecte și clase de referință), dacă sunteți pur și simplu interesat de apelarea prin referință pentru comoditate sintactică (adică nu vă deranjează datele copiate în interior), ați putea emula acest lucru prin atribuirea valorii finale înapoi la variabila exterioară în timp ce vă întoarceți:

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)
  }
}

Apoi, putem declara argumente de „apel prin referință”:

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

Rețineți că, dacă accesați variabilele „prin referință” prin numele lor din exterior (x1, x3) oriunde în interiorul funcției, veți obține valorile lor încă nemodificate din exterior. De asemenea, această implementare gestionează doar nume simple de variabile ca argumente, astfel încât argumentele indexate, cum ar fi f(x[1], ...) nu va funcționa (deși ați putea probabil să implementați acest lucru cu o manipulare a expresiilor puțin mai complicată pentru a evita limitarea assign).

Hugo Raguet

În plus față de celelalte sugestii, puteți scrie, de asemenea, funcții C/C++ care își iau argumentele prin referință și care lucrează pe locși să le apelați direct în R datorită funcției Rcpp (printre altele).Vedeți în special acest răspuns.