Использование функций и сред - PullRequest
3 голосов
/ 15 апреля 2010

После недавних обсуждений здесь (например, 1 , 2 ) я сейчас использую окружения в некоторых моих кодах. У меня вопрос, как мне создать функции, которые изменяют среду в соответствии с ее аргументами? Например:

y <- new.env()
with(y, x <- 1)
f <- function(env,z) {
    with(env, x+z)
}
f(y,z=1)

бросает

Error in eval(expr, envir, enclos) : object 'z' not found

Я использую среды для одновременного разделения двух наборов симуляций (без рефакторинга своего кода, который я написал для одного эксперимента).

Ответы [ 3 ]

8 голосов
/ 15 апреля 2010

Самое простое решение - использовать среду при обращении к объекту:

y <- new.env()
y$x <- 1
f <- function(env,z) {
    env$x+z
}
f(y,z=1)

Вам также необходимо назначить z для вашей среды.

y <- new.env()
with(y, x <- 1)
f <- function(env,z) {
    assign("z", z, envir=env)
    with(env, x+z)
}
f(y,z=1)

Еще одним вариантом будет attach ваша среда, чтобы теперь переменные можно было использовать напрямую.

y <- new.env()
with(y, x <- 1)
f <- function(env,z) {
    attach(env)
    y <- x + z
    detach(env)
    y
}
f(y,z=1)

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

Редактировать

Это интересно, и я не совсем понимаю поведение (т.е. почему z не входит в сферу вызова with). Это как-то связано с созданием изначально среды, из-за которой она выходит за рамки функции, потому что эта версия работает:

f <- function(z) {
    y <- new.env()
    with(y, x <- 1)
    with(y, x+z)
}
f(y,z=1)
3 голосов
/ 16 апреля 2010

Вам нужно всего лишь сделать одно изменение, чтобы ваш пример работал - переопределите вашу функцию, чтобы использовать substitute(), чтобы «исправить» нужные значения в пределах f():

f <- function(env,z) {
    eval(substitute(x+z,list(z=z)), env)
}

Это может быстро стать темным, особенно если учесть, что вы даже можете включить операторы присваивания в substitute() (например, заменить x+z на y <- x+z, но это не совсем уместно), но этот выбор может сделать разработчик ...

Кроме того, вы можете заменить list(z=z) в приведенном выше выражении подстановки на environment() (например, substitute(x+z,environment())), если у вас нет конфликтующих имен переменных между теми, которые переданы в f(), и теми, которые находятся в вашем 'env', но, возможно, вы не захотите заходить слишком далеко.

Редактировать: Вот два других способа, первый из которых предназначен только для демонстрации гибкости в манипулировании средами, а второй более целесообразно использовать.

1) изменить окружающее окружение 'env' (но перед выходом из функции изменить его на исходное значение):

f <- function(env,z) {
  e <- environment(env)
  environment(env) <- environment()
  output <- with(env,x+z)
  environment(env) <- e
  output
}

2) Принудительно вычислять 'z' в текущей среде функции (используя environment()), вместо того, чтобы оставить его свободной переменной после вычисления выражения x+z в 'env'.

f <- function(env,z) {
  with(environment(),with(env,x+z))
}

В зависимости от желаемого порядка разрешения, в случае противоречивых ассоциаций символ-значение - например, если у вас есть «x», определенное как в вашей функциональной среде, так и в среде, которую вы создали, «y» (какое значение «x» делает вы хотите, чтобы это предполагалось?) - вместо этого вы можете определить тело функции как with(env,with(environment(),x+z)).

1 голос
/ 09 февраля 2011
 y <- new.env()
 with(y, x <- 1)
 f <- function(env,z) {
    with(env, x+z)
 }
 f(y,z=1)

обратите внимание на круглые скобки :) Будет работать следующее:

with(env, x)+z
...