【问题标题】:"Capturing" the global environment“捕捉”全球环境
【发布时间】:2021-04-13 22:10:47
【问题描述】:

我正在使用外部包中的函数(我无法修改)。这些函数在全局环境中放置了很多东西,例如包做了类似的事情

the.data <<- data.frame(A=rnorm(10),B=rnorm(10),C=rnorm(10)) ## A sample dataset

package.plot <- function(){
  x.coords <<- the.data$A/the.data$B
  y.coords <<- the.data$C
  plot(x.coords, y.coords)
}

(显然是超简化的例子......这里的关键是 x.coods 和 y.coords 是相当复杂的派生,足够复杂以至于我不想重新编码它们,但发现重用现有代码是有利的)

我想在我自己的脚本中使用这些函数,即用 ggplot 制作相同的图表。当然,第一个显而易见的解决方案是

my.better.plot <- function(){
  package.plot()
  tibble(x.coords,y.coords) %>% ggplot(aes(x=x.coords,y=y.coords))+geom_point() # etc. 
}

但是,这有两个问题:

  1. 我最终绘制了两次(一个小问题,它足够快 不引人注目);
  2. 我用“污染”全球环境 全局 x.coords 和 y.coords

因此,我想在“伪全局”环境中运行 package.plot() 以避免最终得到可能以“不受控制”方式修改的全局变量。

当然,一种解决方法是

my.better.plot <- function(){
  package.plot()
  tibble(x.coords,y.coords) %>% ggplot(aes(x=x.coords,y=y.coords))+geom_point() # etc. 
  rm(x.coords,envir=.GlobalEnv)
}

但是,我更喜欢做类似的事情

my.better.and.cleaner.plot(){
  within.envir(dummy_env,my.better.plot)
}

.. 假设确实有一个“within.envir”函数允许在模拟全局环境中运行它的第二个参数。

这样的事情可能吗?我确实阅读了 http://adv-r.had.co.nz/Environments.html ,但找不到答案……(无论如何,我都没有理解)。奖金问题:如果可能的话,我怎样才能从 dummy_env 中提取 ggplot 的返回值并返回它?

【问题讨论】:

  • "我无法修改" 好吧,你可以而且你可能应该这样做。这可能需要一些工作,但绝对有可能。像这样使用&lt;&lt;- 的包是不可信的。
  • 您在技术上是正确的,但在这种情况下,“不能”更像是外交上的不可能,而不是技术上的不可能。不详述,但它是一个同事写的包,我不能欺负他重写他的代码......

标签: r


【解决方案1】:

不幸的是,这很 hacky,因为 &lt;&lt;- 运算符如果找不到变量名,它将向上遍历环境树(因此你基本上不应该使用它。 一种解决方法是从另一个已经初始化了相关变量的环境中调用该函数。然后它将它分配给这些变量,而不是进一步向上遍历到 globalEnv。不过,您需要事先知道变量名称。

f <- function(x) a <<- x
f(5)
# a = 5 in GlobalEnv
rm(a)
CapturedCall <- function(fun, CapturedVars,...)
{
  stopifnot(is.function(fun))
  SandBox <- new.env()
  for(varName in CapturedVars) assign(varName, NA,SandBox)
  environment(fun) <- SandBox
  fun(...)
}

CapturedCall(f,"a",1)
#Nothing in GlobalEnv

【讨论】:

  • 我认为与上面罗兰的回答基本相似,尽管角度略有不同。谢谢!
【解决方案2】:

这个函数尽可能的避免了副作用:

  library(ggplot2)
  library(magrittr)
  library(tibble)
  my.better.plot <- function(){
    x.coords <- 1
    y.coords <- 1
    environment(package.plot) <- environment()
    
    bmp(tempfile())
    package.plot()
    dev.off()
    
    print(tibble(x.coords,y.coords) %>% ggplot(aes(x=x.coords,y=y.coords))+geom_point()) # etc. 
  }
  
  my.better.plot()
  #creates only the ggplot in the current device

  ls(globalenv())
  #[1] "my.better.plot" "package.plot"   "the.data" 

【讨论】:

  • 确实如此。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-10-23
  • 1970-01-01
  • 2017-01-16
  • 1970-01-01
  • 2020-07-19
  • 2018-07-28
  • 2018-07-12
相关资源
最近更新 更多