在R中是否可以将包环境附加为特定环境(而不是全局环境)的父环境?

在R中是否可以将包环境附加为特定环境(而不是全局环境)的父环境?,r,package,parent,environment,read-eval-print-loop,R,Package,Parent,Environment,Read Eval Print Loop,我正在尝试创建一个用R(源代码)编写的玩具R REPL。理想情况下,我希望REPL本身在R终端中运行,但既不干扰也不依赖于在全局环境中已经评估过的任何内容。不幸的是,我还没有找到解决这个问题的办法。我面临的主要挑战之一是如何连接包环境 根据,由library()和require()附加的包成为全局环境的父级。然而,这意味着,如果我在我的玩具REPL中附加一个包,它将成为全局环境的父级,即使我没有在全局环境中运行它 例如(请注意,R>提示符是“正常”R终端,>>>是我的REPL的“终端”): R>

我正在尝试创建一个用R(源代码)编写的玩具R REPL。理想情况下,我希望REPL本身在R终端中运行,但既不干扰也不依赖于在全局环境中已经评估过的任何内容。不幸的是,我还没有找到解决这个问题的办法。我面临的主要挑战之一是如何连接包环境

根据,由
library()
require()
附加的包成为全局环境的父级。然而,这意味着,如果我在我的玩具REPL中附加一个包,它将成为全局环境的父级,即使我没有在全局环境中运行它

例如(请注意,
R>
提示符是“正常”R终端,
>>>
是我的REPL的“终端”):

R>search()
#[1]“.GlobalEnv”“工具:rstudio”“包:stats”
#[4]“包:图形”“包:GR设备”“包:UTIL”
#[7]“包:数据集”“包:方法”“自动加载”
#[10]“包:基本”
R> replr::replr(env=new.env())#new.env()默认为将全局环境作为父环境
>>>>图书馆(gtfsio)
>>>>rlang::env_父对象(last=emptyenv())
#  [[1]] $ 
#  [[2]] $ 
#  [[3]] $ 
#  [[4]] $ 
#  [[5]] $ 
#  [[6]] $ 
#  [[7]] $ 
#  [[8]] $ 
#  [[9]] $ 
# [[10]] $ 
# [[11]] $ 
# [[12]] $ 
>>>>进口(gtfs)
#错误:缺少参数“path”,没有默认值
>>>>q()
R> 搜索()
#[1]“.GlobalEnv”包:gtfsio“工具:rstudio”
#[4]“包:统计”“包:图形”“包:图形设备”
#[7]“包:utils”“包:数据集”“包:方法”
#[10]“自动加载”包:基本
我们可以看到,我可以使用
gtfsio
import_gtfs()
函数(缺少
path
,但您明白了这一点),但是包也连接到了“main”R终端。如果我尝试使用另一个环境作为新环境的父环境,我甚至无法访问包的函数,因为它无法找到它们,因为包环境成为全局环境的父环境,而不是新环境的父环境:

正在重新启动R会话。。。
R> 搜索()
#[1]“.GlobalEnv”“工具:rstudio”“包:stats”
#[4]“包:图形”“包:GR设备”“包:UTIL”
#[7]“包:数据集”“包:方法”“自动加载”
#[10]“包:基本”
R> replr::replr(env=new.env(parent=baseenv())
>>>>图书馆(gtfsio)
>>>>rlang::env_父母()
# [[1]] $ 
# [[2]] $ 
>>>>进口(gtfs)
#错误:找不到函数“import_gtfs”
>>>>q()
R> 搜索()
#[1]“.GlobalEnv”包:gtfsio“工具:rstudio”
#[4]“包:统计”“包:图形”“包:图形设备”
#[7]“包:utils”“包:数据集”“包:方法”
#[10]“自动加载”包:基本
那么,有没有办法将包环境附加为自定义环境的父环境,而不是全局环境?如果没有,有没有办法解决这个问题

干杯


编辑:

对不起,我应该提供更多关于REPL如何工作的细节

基本上,我只是使用
readline()
读取用户输入,将其解析为表达式,并在指定的环境中对其求值。下面的代码适用于简单的演示:


simple\u repl您只有一个搜索路径,因此无法正确连接到另一个路径

不过,您仍然可以拥有父环境链,我们可以在您的
repl_env
中重新定义
library
,以设置此链

repl_env <- new.env()
with(repl_env, library <- function(package) {
  # fetch repl_env from the inside
  repl_env    <- parent.env(environment())
  # and its parent (.GlobalEnv the first time)
  parent_env <- parent.env(repl_env)
  # create a new env for our package and fill it
  pkg_env <- new.env()
  package <- deparse1(substitute(package))
  object_nms <- getNamespaceExports(package)
  objects    <- mget(object_nms, envir = asNamespace(package))
  list2env(objects, pkg_env)
  # stitch it above repl_env and below repl_env's parent
  parent.env(pkg_env) <- parent_env
  parent.env(repl_env) <- pkg_env
  # base::library returns the search path invisibly but here it woudn't make
  # sense so we just return NULL
  invisible(NULL)
})

simple_repl(repl_env)
>>>> x <- "hello"
>>>> y <- "world"
>>>> library(glue)
>>>> glue("{x} {y}")
#> hello world
>>>> 

# the {glue} package is not on the search path  
search()
#> [1] ".GlobalEnv"        "tools:rstudio"     "package:stats"     "package:graphics" 
#> [5] "package:grDevices" "package:utils"     "package:datasets"  "package:methods"  
#> [9] "Autoloads"         "package:base"  
repl_env>>库(胶水)
>>>>胶水(“{x}{y}”)
#>你好,世界
>>>> 
#{glue}包不在搜索路径上
搜索()
#>[1]“.GlobalEnv”“工具:rstudio”“包:统计”“包:图形”
#>[5]“包:grDevices”“包:utils”“包:数据集”“包:方法”
#>[9]“自动加载”包:基本

使用
repl_env您只有一个搜索路径,因此无法正确附加到另一个路径

不过,您仍然可以拥有父环境链,我们可以在您的
repl_env
中重新定义
library
,以设置此链

repl_env <- new.env()
with(repl_env, library <- function(package) {
  # fetch repl_env from the inside
  repl_env    <- parent.env(environment())
  # and its parent (.GlobalEnv the first time)
  parent_env <- parent.env(repl_env)
  # create a new env for our package and fill it
  pkg_env <- new.env()
  package <- deparse1(substitute(package))
  object_nms <- getNamespaceExports(package)
  objects    <- mget(object_nms, envir = asNamespace(package))
  list2env(objects, pkg_env)
  # stitch it above repl_env and below repl_env's parent
  parent.env(pkg_env) <- parent_env
  parent.env(repl_env) <- pkg_env
  # base::library returns the search path invisibly but here it woudn't make
  # sense so we just return NULL
  invisible(NULL)
})

simple_repl(repl_env)
>>>> x <- "hello"
>>>> y <- "world"
>>>> library(glue)
>>>> glue("{x} {y}")
#> hello world
>>>> 

# the {glue} package is not on the search path  
search()
#> [1] ".GlobalEnv"        "tools:rstudio"     "package:stats"     "package:graphics" 
#> [5] "package:grDevices" "package:utils"     "package:datasets"  "package:methods"  
#> [9] "Autoloads"         "package:base"  
repl_env>>库(胶水)
>>>>胶水(“{x}{y}”)
#>你好,世界
>>>> 
#{glue}包不在搜索路径上
搜索()
#>[1]“.GlobalEnv”“工具:rstudio”“包:统计”“包:图形”
#>[5]“包:grDevices”“包:utils”“包:数据集”“包:方法”
#>[9]“自动加载”包:基本

使用
repl\u env很难进行测试,因为我们无法运行您的repl。有没有可能把它转换成我们可以轻松测试的函数?你会有两种效果,在这里,外部的事情会影响内部发生的事情,反之亦然。通常避免这种情况的方法是每次都得到一个新的、孤立的R实例。我在问题中添加了一个简单的REPL进行测试。@alistaire是的,我知道有两种效果,可能运行单独的进程是防止这种效果的一种好方法,但如果可能的话,我希望保持相同的进程。无论如何,感谢您指导callr,我将检查代码,看看它是否带来了一些好主意。
library()
和company有一个
pos
参数,允许您以不同的顺序附加包/名称空间,但是在R会话中只有一个搜索路径和一组加载的名称空间。包不仅仅是环境;还有更多的事情要做。您可以尝试覆盖并复制infra包或在出口处清理,但这两种方法都有问题。A.