0

在函数内部,我正在采购一个脚本:

f <- function(){
  source("~/Desktop/sourceme.R") # source someone elses script
  # do some stuff to the variables read in
}
f()
search() # library sourceme.R attaches is all the way in the back!

不幸的是,我采购的脚本并没有完全在我的控制之下。他们调用library(somePackage),它污染了搜索路径。

如果作者sourceme.R期望他/她附加的包处于顶层/接近全局环境,这主要是一个问题。如果我自己附加了一些包来掩盖他/她期望可用的一些功能名称,那可不好。

有没有办法我可以获取脚本,但以某种方式制作我自己的临时搜索路径,在函数完成运行后“重置”?

4

2 回答 2

2

我会考虑使用包在单独的 R 进程中获取脚本,callr然后返回由源文件创建的环境。

通过使用单独的 R 进程,这将防止您的搜索路径被污染。我猜在你想要的全局环境中可能会有一些副作用(比如定义变量的新函数)。函数的local参数source允许您指定应在何处执行已解析的脚本。如果从其他 R 进程返回此环境,则可以访问所需的任何结果。

不确定你的是什么样子,但说我有这个文件可以修改搜索路径:

# messWithSearchPath.R

library(dplyr)

a <- data.frame(groupID = rep(1:3, 10), value = rnorm(30))

b <- a %>% 
  group_by(groupID) %>% 
  summarize(agg = sum(value))

从我的顶级脚本中,我将编写一个包装函数以在新环境中获取它并callr执行此函数:

RogueScript <- function(){
  
  rogueEnv <- new.env()
  
  source("messWIthSearchPath.R", local = rogueEnv)
  
  rogueEnv
  
}

before <- search()

scriptResults <- callr::r(RogueScript)

scriptResults$b
#>   groupID       agg
#> 1       1 -2.871642
#> 2       2  3.368499
#> 3       3  1.159509

identical(before, search())
#> [1] TRUE

如果脚本有其他副作用(例如设置选项或建立外部连接),则此方法可能不起作用。根据他们打算做什么,可能会有解决方法,但如果您只想创建变量/函数,这应该可以工作。它还可以防止脚本相互冲突,而不仅仅是您的顶级脚本。

于 2020-09-04T18:33:36.450 回答
1

一种方法是“快照”您当前的搜索路径并稍后尝试返回它:

search.snapshot <- local({
  .snap <- character(0)
  function(restore = FALSE) {
    if (restore) {
      if (is.null(.snap)) {
        return(character(0))
      } else {
        extras <- setdiff(search(), .snap)
        # may not work if DLLs are loaded
        for (pkg in extras) {
          suppressWarnings(detach(pkg, character.only = TRUE, unload = TRUE))
        }
        return(extras)
      }
    } else .snap <<- search()
  }
})

在行动:

search.snapshot()                                  # store current state
get(".snap", envir = environment(search.snapshot)) # view snapshot
#  [1] ".GlobalEnv"        "ESSR"              "package:stats"    
#  [4] "package:graphics"  "package:grDevices" "package:utils"    
#  [7] "package:datasets"  "package:r2"        "package:methods"  
# [10] "Autoloads"         "package:base"     
library(ggplot2)
library(zoo)
# Attaching package: 'zoo'
# The following objects are masked from 'package:base':
#     as.Date, as.Date.numeric
library(dplyr)
# Attaching package: 'dplyr'
# The following objects are masked from 'package:stats':
#     filter, lag
# The following objects are masked from 'package:base':
#     intersect, setdiff, setequal, union
search()
#  [1] ".GlobalEnv"        "package:dplyr"     "package:zoo"      
#  [4] "package:ggplot2"   "ESSR"              "package:stats"    
#  [7] "package:graphics"  "package:grDevices" "package:utils"    
# [10] "package:datasets"  "package:r2"        "package:methods"  
# [13] "Autoloads"         "package:base"     

search.snapshot(TRUE)                              # returns detached packages
# [1] "package:dplyr"   "package:zoo"     "package:ggplot2"

search()
#  [1] ".GlobalEnv"        "ESSR"              "package:stats"    
#  [4] "package:graphics"  "package:grDevices" "package:utils"    
#  [7] "package:datasets"  "package:r2"        "package:methods"  
# [10] "Autoloads"         "package:base"     

我有点自信(未经验证)这并不总是适用于所有包,可能是由于依赖项和/或加载的 DLL。您可以尝试添加force=TRUEdetach通话中,但不确定这是否会更好,或者可能有其他不良副作用。

于 2020-09-04T16:56:28.003 回答