Можете ли вы передать по ссылке в R? - PullRequest
62 голосов
/ 09 апреля 2010

Можете ли вы пройти по ссылке с "R"? например, в следующем коде:

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

вывод

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

но я бы хотел, чтобы это было

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

возможно ли это?

Ответы [ 9 ]

52 голосов
/ 09 апреля 2010

Нет .

Объекты в операторах присваивания неизменны. R скопирует объект не , а просто ссылка.

> 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

( условие : приведенное выше утверждение верно для R примитивов , например, векторов, матриц), а также для функций ; Я не могу с уверенностью сказать, верно ли это для всех R объектов - только для большинства из них, а также для подавляющего большинства наиболее часто используемых).

Если вам не нравится это поведение, вы можете отказаться от него с помощью пакета R. Например, существует пакет R под названием R.oo , который позволяет имитировать поведение передачи по ссылке; R.oo доступен на CRAN .

40 голосов
/ 01 августа 2012

Обратите внимание, что если вы надеетесь использовать передачу по ссылке просто для того, чтобы избежать влияния на производительность копирования объекта, который не был изменен (как это обычно бывает в других языках с постоянными ссылками), R делает это автоматически:

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
20 голосов
/ 12 марта 2012

Как уже указывалось несколькими, это можно сделать с помощью объектов класса environment. Существует формальный подход, основанный на использовании environment s. Он называется Reference Classes и упрощает работу. Проверьте ?setRefClass на главной странице справки. Также описывается, как использовать формальные методы со ссылочными классами.

Пример

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

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

array = c(instance1,instance2)

instance1$name <- "World!"

выход

> 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"
16 голосов
/ 09 апреля 2010

Передача по ссылке возможна для environment с. Чтобы использовать их, в основном, когда вы создаете объект, вам также необходимо создать слот среды. Но я думаю, что это громоздко. Посмотри на Передача по ссылке для S4. и Указатели и передача по ссылке в R

5 голосов
/ 31 января 2012

R теперь имеет библиотеку, которая позволяет вам выполнять ООП, используя ссылки. См. ReferenceClasses , который является частью пакета методов.

4 голосов
/ 05 января 2011

На самом деле пакет R.oo имитирует поведение передачи по ссылке с использованием сред.

3 голосов
/ 10 ноября 2015

Как уже говорилось, для классов S4 это невозможно.Но теперь R предоставляет возможность с библиотекой R6 , которая называется reference classes.См. официальную документацию

2 голосов
/ 20 февраля 2014

В дополнение к другим ответам здесь, которые на самом деле передают ваш объект по ссылке (environment объекты и ссылочные классы), если вы просто заинтересованы в вызове по ссылке для удобства синтаксиса (т.е. вы не возражаете против ваших данных, скопированных внутрь), вы можете эмулировать это, присваивая окончательное значение внешней переменной при возврате:

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

Тогда мы можем объявить аргументы "call-by-reference":

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

Обратите внимание, что если вы обращаетесь к переменным «по ссылке» по их внешним именам (x1, x3) в любом месте внутри функции, вы получите их еще неизмененные значения извне. Кроме того, эта реализация обрабатывает только простые имена переменных в качестве аргументов, поэтому индексированные аргументы, такие как f(x[1], ...), не будут работать (хотя вы, вероятно, могли бы реализовать это с немного более сложной манипуляцией с выражениями, чтобы обойти ограниченное assign).

1 голос
/ 23 августа 2017

Помимо других предложений, вы также можете написать функции C / C ++, берущие их аргументы по ссылке и работающие на месте , и вызывать их непосредственно в R благодаря Rcpp (среди прочих) , См. В частности этот ответ .

...