Использование функций и сред
После недавних обсуждений здесь (например, 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 ответа
Самое простое решение - использовать среду при обращении к объекту:
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)
Вам нужно всего лишь сделать одно изменение, чтобы ваш пример работал - переопределите свою функцию для использования 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))
,
y <- new.env()
with(y, x <- 1)
f <- function(env,z) {
with(env, x+z)
}
f(y,z=1)
обратите внимание на круглые скобки:) Следующее будет работать:
with(env, x)+z