Использование функций и сред

После недавних обсуждений здесь (например, 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

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

Ответ 1

Простейшим решением является использование среды при обращении к объекту:

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)

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

Edit:

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

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

Ответ 2

Вам нужно только сделать одно изменение, чтобы ваш пример работал - переопределите свою функцию, чтобы использовать 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)).

Ответ 3

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

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

with(env, x)+z