R: Передавать по ссылке
Можете ли вы передать ссылку "R"?
например, в следующем коде:
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
вывод
> 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"
Возможно ли это?
Спасибо
Пьер
Ответы
Ответ 1
Нет.
Объекты в операторах присваивания неизменяемы. 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.
Ответ 2
Обратите внимание, что если вы хотите использовать pass-by-reference просто, чтобы избежать последствий для копирования объекта, который не был изменен (как это часто встречается на других языках с постоянными ссылками), 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
Ответ 3
Передача по ссылке возможна для environment
s. Вы можете попытаться использовать их: в основном, когда вы создаете объект, вам также понадобится создать слот среды. Но я думаю, что это громоздко. Посмотрите Указатели и прохождение по ссылке в R и Перейдите по ссылке для S4.
Ответ 4
Как уже указывалось ранее, это можно сделать с помощью объектов класса 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"
Ответ 5
На самом деле пакет R.oo эмулирует поведение pass-by-reference, используя среды.
Ответ 6
У R теперь есть библиотека, которая позволяет вам делать ООП, используя ссылки. См. ReferenceClasses, который является частью пакета методов.
Ответ 7
В дополнение к другим ответам здесь, которые фактически передают ваш объект по ссылке (environment
objects и Reference Classes), если вас просто интересует вызов-ссылка для синтаксического удобства (т.е. вы не против своего данные, скопированные внутри), вы можете эмулировать это, назначив окончательное значение обратно внешней переменной при возврате:
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)
}
}
Затем мы можем объявить аргументы "по вызову":
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
).
Ответ 8
Как утверждали другие, это невозможно для классов S4. Но R теперь предоставляет возможность библиотеки R6, называемой ссылкой. См. официальная документация
Ответ 9
В дополнение к другим предложениям вы также можете написать функции C/С++, используя свои аргументы по ссылке и работая на месте, и называть их непосредственно в R благодаря Rcpp
(среди прочего).
См., В частности, этот ответ.