Как R обрабатывает объект при вызове функции? - PullRequest
8 голосов
/ 24 сентября 2011

У меня есть опыт работы с Java и Python, и я недавно изучаю R.

Сегодня я обнаружил, что R, кажется, обрабатывает объекты совершенно иначе, чем Java и Python.

Например, следующий код:

x <- c(1:10)
print(x)
sapply(1:10,function(i){
            x[i] = 4
        })
print(x)

Код дает следующий результат:

[1]  1  2  3  4  5  6  7  8  9 10
[1]  1  2  3  4  5  6  7  8  9 10

Но я ожидаю, что вторая строка вывода будет равна «4», так как я изменил вектор в функции sapply.

Значит ли это, что R делает копии объектов при вызове функции вместо ссылки на объекты?

Ответы [ 5 ]

18 голосов
/ 24 сентября 2011

x определяется в глобальной среде, а не в вашей функции.

Если вы попытаетесь изменить нелокальный объект, такой как x, в функции, то R сделает копию объекта и изменит копию так, что каждый раз, когда вы запускаете свою анонимную функцию, создается копия x и его i-й компонент имеет значение 4. Когда функция завершает работу, созданная копия исчезает навсегда. Оригинал x не изменяется.

Если мы напишем x[i] <<- i или если мы напишем x[i] <- 4; assign("x", x, .GlobalEnv), тогда R напишет это обратно. Другим способом обратной записи было бы установить e, скажем, в среду, в которой хранится x, и сделать это:

e <- environment()
sapply(1:10, function(i) e$x[i] <- 4)

или, возможно, это:

sapply(1:10, function(i, e) e$x[i] <- 4, e = environment())

Обычно никто не пишет такой код в R. Скорее, он выдает результат в виде вывода функции, подобной этой:

x <- sapply(1:10, function(i) 4)

(На самом деле в этом случае можно написать x[] <- 4.)

ДОБАВЛЕНО:

Используя proto package , это можно сделать, когда метод f устанавливает i-й компонент свойства x в 4.

library(proto)

p <- proto(x = 1:10, f = function(., i) .$x[i] <- 4)

for(i in seq_along(p$x)) p$f(i)
p$x

ДОБАВЛЕНО:

Добавлен выше еще один параметр, в котором мы явно передаем среду, в которой хранится x.

7 голосов
/ 24 сентября 2011

Да, вы правы.Проверьте определение языка R: 4.3.3 Оценка аргумента

AFAIK, R на самом деле не копирует данные, пока вы не попытаетесь их изменить, следуя, таким образом, Copy-при записи семантика.

3 голосов
/ 24 сентября 2011

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

Как указывает @DWin, эта скопированная версия x, которая была изменена , , возвращенный вызовом sapply(), ваш заявленный вывод не тот, который я получаю:

> x <- c(1:10)
> print(x)
 [1]  1  2  3  4  5  6  7  8  9 10
> sapply(1:10,function(i){
+             x[i] = 4
+         })
 [1] 4 4 4 4 4 4 4 4 4 4
> print(x)
 [1]  1  2  3  4  5  6  7  8  9 10

Очевидно, код сделал почти то, что вы думали.Проблема в том, что выходные данные из sapply() не были назначены объекту и, следовательно, распечатаны, а затем отброшены.

Причина, по которой вы работаете с кодом, связана с правилами области видимости R. Вы действительно должны передатьдля функции в качестве аргументов любые объекты, которые нужны функции.Однако, если R не может найти объект, локальный для функции, он будет искать в родительской среде объект, соответствующий имени, а затем, при необходимости, в родительской среде этой среды, в конечном итоге затрагивая глобальную среду, рабочее пространство.Таким образом, ваш код работает, потому что он в конечном итоге нашел x для работы, но был немедленно скопирован, эта копия возвращена в конце вызова sapply().

Это копирование во многих случаях требует времени и памяти,Это одна из причин, по которой люди думают, что петли for медленны в R;они не выделяют память для объекта перед заполнением его циклом.Если вы не выделяете память, R должен изменить / скопировать объект, чтобы добавить следующий результат цикла.

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

> a <- new.env()
> a
<environment: 0x1af2ee0>
> b <- 4
> assign("b", b, env = a)
> a$b
[1] 4
> c <- a ## copy the environment to `c`
> assign("b", 7, env = c) ## assign something to `b` in env `c`
> c$b ## as expected
[1] 7
> a$b ## also changed `b` in `a` as `a` and `c` are actually the same thing
[1] 7

Если вы понимаете такие вещи, прочитайте руководство R Определение языка , которое охватывает многие деталио том, что происходит под капотом в R.

0 голосов
/ 20 сентября 2013

Если вы хотите изменить «глобальный» объект изнутри функции, вы можете использовать нелокальное назначение.

x <- c(1:10)
# [1]  1  2  3  4  5  6  7  8  9 10
print(x)
sapply(1:10,function(i){
            x[i] <<- 4
        })
print(x)
# [1] 4 4 4 4 4 4 4 4 4 4

Хотя в этом конкретном случае вы можете просто сделать его более компактным, как x[]<-4

Это, кстати, одна из приятных особенностей R - вместо sapply(1:10,function(i) x[i] <<- 4 или for(i in 1:10) x[i]<-4 (for не является функцией, поэтому вам не нужно <<- здесь), вы можете просто напишите x[]<-4 :)

0 голосов
/ 24 сентября 2011

Вам необходимо присвоить вывод sapply объекту, иначе он просто исчезнет.(На самом деле вы можете восстановить его, так как он также назначен на .Last.value)

x <- c(1:10)
print(x)
 [1]  1  2  3  4  5  6  7  8  9 10
x <- sapply(1:10,function(i){
             x[i] = 4
        })
print(x)
 [1] 4 4 4 4 4 4 4 4 4 4
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...