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

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

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


person Eduardo Leoni    schedule 15.04.2010    source источник
comment
Есть ли причина использовать with? В противном случае вы можете использовать env$x+z.   -  person Marek    schedule 15.04.2010
comment
В своей функции я ссылаюсь на множество объектов внутри среды, отсюда with. Но это хорошее решение, и я мог бы им воспользоваться.   -  person Eduardo Leoni    schedule 15.04.2010
comment
Что ты пытаешься сделать? Я подозреваю, что все, что вы пытаетесь достичь, будет лучше всего делать без явной ссылки на среду. В 15+ написанных мною пакетах и ​​в многочисленных анализах данных, которые я провел, мне приходилось явно использовать среды лишь несколько раз.   -  person hadley    schedule 15.04.2010
comment
@hadley, я уверен, что есть способ получше. Но у меня есть крайний срок, и, как я уже сказал, среда позволяет мне переписать меньшую часть моего кода.   -  person Eduardo Leoni    schedule 15.04.2010
comment
Я понимаю важность крайних сроков, но если вы пойдете по этому пути, ваш код будет становиться все труднее и труднее для понимания и все труднее и труднее адаптировать, когда у вас появятся новые требования.   -  person hadley    schedule 15.04.2010
comment
@hadley: На самом деле код на удивление чистый. Я делаю MCMC для двух разных наборов данных (лет). Мне нужны только резюме, поэтому нет смысла размещать модели в одной и той же матрице. Используя среды, мне не нужно называть такие вещи, как data_2002, mcmc_2002, b_2002 и т. Д. Но я уверен, что есть другие, возможно, лучшие способы справиться с этим. Спасибо за комментарии / наставления.   -  person Eduardo Leoni    schedule 15.04.2010


Ответы (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)
person Shane    schedule 15.04.2010
comment
Спасибо. Я использовал решение с прикрепленным файлом. Но когда функция обнаруживает ошибку перед отключением, я получаю типичные проблемы с подключением. Назначьте работы. Я бы предпочел, чтобы z был временным (как в обычной функции). Думаю, у меня не может быть и того, и другого. - person Eduardo Leoni; 15.04.2010
comment
См. Мой обновленный ответ. Кроме того, если вы столкнетесь с проблемой, оберните все в блоке try или tryCatch и отключите его, несмотря ни на что. - person Shane; 15.04.2010
comment
По какой причине z отсутствует в пути поиска в коде OP? Из документации для with (): среда имеет среду вызывающего абонента в качестве родителя. Кроме того, с (y, x + z), похоже, работает на верхнем уровне. Я несколько озадачен, почему это не удается при вызове функции. - person Leo Alekseyev; 15.04.2010
comment
Шейн, есть ли способ сослаться на область действия функции? - person Eduardo Leoni; 15.04.2010
comment
Эдуардо: это отличный вопрос. Я не знаю способа, хотя может кто знает? - person Shane; 15.04.2010
comment
@Leo: Это тоже отличный вопрос. Я также ожидал, что z будет находиться в области действия функции, а область действия функции будет родительской для вызова with. - person Shane; 15.04.2010
comment
Эдуардо / Шейн: ну, 'sys.frame' возвращает вызывающую среду (а 'sys.frames' вернет все среды в стеке). - person doug; 15.04.2010
comment
Причина в том, что когда 'z' оценивается в 'env' и не обнаруживается, следующее место, куда он смотрит, - это окружающая среда / область видимости. Охватывающая область - это не f (), а глобальная среда, потому что лексические или статические правила области видимости говорят, что включающая область видимости определяется во время создания (следовательно, статическая), а не при оценке (динамическая область). - person hatmatrix; 16.04.2010
comment
Что касается моего предыдущего комментария, охватывающая область - это не f (), а глобальная среда - это в случае исходной формулировки. :-П - person hatmatrix; 16.04.2010
comment
Кроме того, вы можете ссылаться на область действия функции с помощью environment (), но не работает, когда вы вызываете ее в вызове with () внутри функции. - person hatmatrix; 16.04.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)).

person hatmatrix    schedule 16.04.2010

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

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

with(env, x)+z
person Kenn    schedule 09.02.2011