лексическая область видимости и окружения в R - PullRequest
0 голосов
/ 30 апреля 2020

У меня есть следующий фрагмент кода, вдохновленный моим исходным кодом:

func2 <- function(foos) {
  for (foo in foos)
    print(eval(parse(text = foo)))
  return(foos)
}

func1 <- function(vec) {
  text3_obj <- 'text3'
  vec <- c(vec, c('text3_obj'))
  return(func2(vec))
}

text1_obj <- 'text1'
text2_obj <- 'text2'
func1(c('text1_obj', 'text2_obj'))

Здесь в основном коде я создаю 2 объекта (text1_obj & text2_obj) и передаю их имена func1(). Эта функция после добавления другого объекта в вектор вызывает func2(). В func2() я просто печатаю значения объектов. Ниже приведен вывод этого кода:

[1] "text1"
[1] "text2"
Error in eval(parse(text = foo)) : object 'text3_obj' not found

После перехода в режим отладки я понял, что даже exists('text3_obj') изнутри func2() выдает ошибку. поэтому у меня есть 2 вопроса:

  1. Даже если func1() является непосредственным родительским окружением func2(); почему text3_obj не разбирается внутри func2(). Даже более высокие глобальные переменные среды могут быть проанализированы, например, text1_obj.
  2. Почему явное именование среды в режиме отладки работает подобно charm, например, exists('text3_obj', where = parent.frame()) и eval(parse(text = 'text3_obj'), parent.frame())

1 Ответ

3 голосов
/ 01 мая 2020

Вы путаете «среду вызова» с «окружающей средой». Ознакомьтесь с этими терминами в книге Хэдли «Advanced R.»

http://adv-r.had.co.nz/Environments.html

«Среда вызова» - это среда, из которой была вызвана функция, и она возвращено к сожалению названной функцией parent.frame. Однако вызывающая среда не используется для лексической области видимости.

«Окружающая среда» - это среда, в которой была создана функция и которая используется для лексической области видимости. Вы создали как func1, так и func2 в глобальной среде. Таким образом, глобальная среда является «окружающей средой» для обеих функций и будет использоваться для лексической области видимости независимо от среды вызова !!

Если вы хотите func2 использовать выполнение В среде func1 для лексической области видимости у вас есть (как минимум) два варианта. Вы можете создать func2 в пределах func1

func1 <- function(vec) {

  func2 <- function(foos) {
    for (foo in foos)
      print(eval(parse(text = foo)))
    return(foos)
  }

  text3_obj <- 'text3'
  vec <- c(vec, c('text3_obj'))
  return(func2(vec))
}

, тогда ваш тест будет работать как положено:

> text1_obj <- 'text1'
> text2_obj <- 'text2'
> func1(c('text1_obj', 'text2_obj'))
[1] "text1"
[1] "text2"
[1] "text3"
[1] "text1_obj" "text2_obj" "text3_obj"

В качестве альтернативы, вы можете создать func2 и переназначить его "замкнутую среду" из func1.

func2 <- function(foos) {
  for (foo in foos)
    print(eval(parse(text = foo)))
  return(foos)
}

func1 <- function(vec) {
  text3_obj <- 'text3'
  vec <- c(vec, c('text3_obj'))
  environment(func2) <- environment()
  return(func2(vec))
}

Это также будет работать, как и ожидалось.

Интересный лакомый кусочек, который я обнаружил во время написания демонстрационного кода ... Похоже, что когда вы переназначаете в среде func2 изнутри func1, R создает копию func2 в среде выполнения func1. К тому времени, когда вы вернетесь к консоли, окружающая среда исходного func2 останется неизменной. Свидетель:

a = function() {
  print(identical(environment(a), globalenv()))
}

b = function(x) {
  environment(a) <- environment()
  a()
}

Тест a() и b():

> a()
[1] TRUE
> b()
[1] FALSE
> a()
[1] TRUE
>

Это было не то, что я ожидал, но, похоже, действительно отличное поведение со стороны R. Если это в противном случае окружающая среда a() была бы навсегда изменена на среду выполнения b(), и FALSE должен был бы быть возвращен при втором вызове a().

Если Оказывается, вы можете принудительно изменить исходное значение a() в глобальной среде, используя <<-:

a = function() {
  print(identical(environment(a), globalenv()))
}

b = function(x) {
  # set a variable in the execution environment of b() for use later...
  montePython = "I'm not dead yet!!"
  # change the enclosing environment of a() in the global environment
  # rather than making a local copy of a() in b()'s execution environment.
  environment(a) <<- environment()
  a()
}

Test a() и b():

> a()
[1] TRUE
> b()
[1] FALSE
> a()
[1] FALSE
>

Интересно, что это означает, что (обычно временная) среда выполнения b() сохраняется в памяти даже после завершения b(), потому что a() все еще ссылается на среду, поэтому она не может быть собрана сборщиком мусора. Свидетель:

> environment(a)$montePython
[1] "I'm not dead yet!!"
...