R: копировать / перемещать одну среду в другую - PullRequest
18 голосов
/ 01 апреля 2012

Я хотел бы спросить, возможно ли одновременно копировать / перемещать все объекты одной среды в другую.Например:

f1 <- function() {
    print(v1)
    print(v2)
}

f2 <- function() {
    v1 <- 1
    v2 <- 2

    # environment(f1)$v1 <- v1         # It works
    # environment(f1)$v2 <- v2         # It works

    environment(f1) <- environment(f2) # It does not work
}

f2()
f1()

TNX, заранее

Ответы [ 6 ]

25 голосов
/ 01 апреля 2012

Кажется, что вы можете сделать как минимум 3 разные вещи:

  1. Клонирование среды (создание точной копии)
  2. Копировать содержимое одной среды в другую среду
  3. В одной среде

Для клонирования:

# Make the source env
e1 <- new.env()
e1$foo <- 1
e1$.bar <- 2   # a hidden name
ls(e1) # only shows "foo"

# This will clone e1
e2 <- as.environment(as.list(e1, all.names=TRUE))

# Check it...
identical(e1, e2) # FALSE
e2$foo
e2$.bar

Чтобы скопировать содержимое, вы можете делать то, что показывал @gsk. Но опять же, флаг all.names полезен:

# e1 is source env, e2 is dest env
for(n in ls(e1, all.names=TRUE)) assign(n, get(n, e1), e2)

Делить окружение - это то, что делал @koshke. Это, вероятно, часто гораздо полезнее. Результат такой же, как при создании локальной функции:

f2 <- function() {
  v1 <- 1 
  v2 <- 2

  # This local function has access to v1 and v2
  flocal <- function() {
    print(v1)
    print(v2)
  }

  return(flocal)
} 

f1 <- f2()
f1() # prints 1 and 2 
9 голосов
/ 01 апреля 2012

Попробуйте это:

f2 <- function() {
    v1 <- 1
    v2 <- 2
    environment(f1) <<- environment()
}
5 голосов
/ 01 апреля 2012

Вы можете использовать assign:

f1 <- function() {
  print(v1)
  print(v2)
}

f2 <- function() {
  v1 <- 1
  v2 <- 2

  for(obj in c("v1","v2")) {
    assign(obj,get(obj),envir=f1.env)
  }
}

Если вы не хотите перечислять объекты, ls() принимает аргумент среды.

И вам придется выяснить, как сделать f1.env средой, указывающей внутри f1: -)

2 голосов
/ 01 ноября 2015

Я использую эту функцию в своем пакете для копирования объектов:

copyEnv <- function(from, to, names=ls(from, all.names=TRUE)) {
  mapply(assign, names, mget(names, from), list(to), 
         SIMPLIFY = FALSE, USE.NAMES = FALSE)
  invisible(NULL)
}
1 голос
/ 27 мая 2014

Для этого:

environment(f1) <- environment(f2) # It does not work

Откройте среду f1 и выполните команду:

ls(load(f2))
0 голосов
/ 13 ноября 2018

Метод "клонирования", опубликованный Томми, не сделает истинный (глубокий) клон, если e1 содержит имена, которые ссылаются на другие среды.Например, если e1$nestedEnv ссылается на среду, e2$nestedEnv будет ссылаться на ту же среду, а не на копию этой среды.Таким образом, имя e1$nestedEnv$bar будет ссылаться на ту же ячейку памяти, что и e2$nestedEnv$bar, и любое новое значение, присвоенное e1$nestedEnv$bar, будет отражено и для e2$nestedEnv$bar.Это может быть желательным поведением, но вызов e2 клона e1 может вводить в заблуждение.

Вот функция, которая позволит пользователю сделать либо копию среды, но и копировать любые вложенные среды.(«глубокий клон», используя deep = TRUE), или просто используйте метод, предложенный Томми, для копирования среды, сохраняя при этом исходные ссылки на любые вложенные среды (используя deep = FALSE).

The Deep =Метод TRUE использует rapply для рекурсивного вызова cloneEnv во вложенной среде в пределах envir, для стольких уровней, сколько вложенная среда.Так что, в конце концов, он рекурсивно вызывает rapply, что немного раздражает, но работает довольно хорошо.

Обратите внимание, что, если вложенная среда содержит имя, которое ссылается на родительскую среду, использование «глубокого» метода никогда не вернется из рекурсивных вызовов.Если бы я мог найти способ проверить это, я бы включил его ...

Обратите также внимание, что среды могут иметь атрибуты, поэтому копирование атрибутов будет необходимо для настоящего клона, которыйЭто решение также обращается к.

cloneEnv <- function(envir, deep = T) {
  if(deep) {
    clone <- list2env(rapply(as.list(envir, all.names = TRUE), cloneEnv, classes = "environment", how = "replace"), parent = parent.env(envir))
  } else {
    clone <- list2env(as.list(envir, all.names = TRUE), parent = parent.env(envir))
  }
  attributes(clone) <- attributes(envir)
  return(clone)
}

Пример:

Создать среду e1, которая также содержит вложенную среду:

e1 <- new.env()
e1$foo <- "Christmas"
e1$nestedEnv <- new.env()
e1$nestedEnv$bar <- "New Years"

Показать значения для foo и bar:

e1$foo
[1] "Christmas"
e1$nestedEnv$bar
[1] "New Years"

Сделать глубокий клон (т. Е. e2 содержит делает копию nestedEnv)

e2 <- cloneEnv(e1, deep = TRUE)

nestedEnv in e1ссылается на среду, отличную от nestedEnv в e2:

identical(e1$nestedEnv, e2$nestedEnv)
[1] FALSE

Но значения одинаковы, поскольку e2$nestedEnv является копией e1$nestedEnv:

e2$foo
[1] "Christmas"
e2$nestedEnv$bar
[1] "New Years"

Изменитьзначения в e2:

e2$foo <- "Halloween"
e2$nestedEnv$bar <- "Thanksgiving"

И значения в e1 также остаются неизменными, поскольку e1$nestedEnv указывает на среду, отличную от e2$nestedEnv:

e1$foo
[1] "Christmas"
e2$foo
[1] "Halloween"

e1$nestedEnv$bar
[1] "New Years"
e2$nestedEnv$bar
[1] "Thanksgiving"

Теперь заново создайте e1, используя метод Томми:

e2 <- cloneEnv(e1, deep = FALSE)

nestedEnv в e2 указывает на то же окружение, что и nestedEnv в e1:

identical(e1$nestedEnv, e2$nestedEnv)
[1] TRUE

Обновите значения в e2 и e2 nestedEnv:

e2$foo <- "Halloween"
e2$nestedEnv$bar <- "Thanksgiving"

Значения foo являются независимыми:

e1$foo
[1] "Christmas"
e2$foo
[1] "Halloween"

но при обновлении значения e2 bar также обновлены e1 bar, поскольку e1$nestedEnv и e2$nestedEnv ссылаются (указывают на) на одну и ту же среду.

e1$nestedEnv$bar
[1] "Thanksgiving"
e2$nestedEnv$bar
[1] "Thanksgiving"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...