繁体   English   中英

自定义环境中的范围(功能)

[英]Scoping (functions) in custom environment

我有一个特殊的(虚拟的)function,我想在沙盒环境中使用它:

disable.system.call <- function(...) {
    mc <- match.call()
    if (grepl('system', deparse(mc[[2]])))
        stop('NONO')
    eval(mc, env = .GlobalEnv)        
}

它没有做任何特别的事情,只是检查第一个参数的名称中是否包含system字。 这只是一个 POC 示例。

我稍后做什么:我将这个简单的函数分配给一些base函数和stats函数,以查看计算的表达式是否确实包含system字作为第一个参数。 例如:

e <- new.env()
eval(parse(text = 'model.frame <- disable.system.call'), envir = e)

这工作起来非常酷,因为没有system内部的调用就像一个魅力,但过滤器工作:

> eval(parse(text = 'model.frame("1 ~ 1")'), envir = e)
  1
1 1
> eval(parse(text = 'model.frame(\'1 ~ system("ls -la")\')'), envir = e)
Error in model.frame("1 ~ system(\"ls -la\")") : NONO

它甚至使用lm调用,调用model.frame在内部找到一个像字符串这样的公式:

> eval(parse(text = 'lm(\'1 ~ system("ls -la")\')'), envir = e)
Error in model.frame(formula = "1 ~ system(\"ls -la\")", drop.unused.levels = TRUE) : 
  NONO

我进一步尝试 go 并将非常简单的 function ( disable.system.call ) 分配给从as.formula调用的model.frame 不幸的是我没有得到到目前为止:

> e <- new.env()
> eval(parse(text = 'as.formula <- disable.system.call'), envir = e)
> eval(parse(text = 'as.formula("1 ~ 1")'), envir = e)
1 ~ 1
> eval(parse(text = 'as.formula(\'1 ~ system("ls -la")\')'), envir = e)
Error in as.formula("1 ~ system(\"ls -la\")") : NONO
> eval(parse(text = 'model.frame(\'1 ~ system("ls -la")\')'), envir = e)
  1 system("ls -la")
1 1                0
> eval(parse(text = 'lm(\'1 ~ system("ls -la")\')'), envir = e)

Call:
lm(formula = "1 ~ system(\"ls -la\")")

Coefficients:
     (Intercept)  system("ls -la")  
           1                NA  

据我所知model.frame正在调用as.formula但这不起作用(正如您从上面的输出中看到的那样)。 我很确定这不是因为model.frame在自定义环境中调用stats::as.formula就像lmmodel.frame上面调用的那样。

任何提示和想法都会受到欢迎!

尽管您怀疑情况并非如此,但正在调用stats:::model.frame.default ,而不是环境e中的自定义版本。 (这当然是您通常期望打包函数的行为。在您的第一个示例中看到的奇怪范围是一种特殊情况,因为lm()使用了“非标准评估”,这在我的答案的底部)。

如下所示,您可以使用trace()查看在每种情况下调用的as.formula()版本:

disable.system.call <- function(...) {
    mc <- match.call()
    if (grepl('system', deparse(mc[[2]])))
        stop('NONO')
    eval(mc, env = .GlobalEnv)        
}
e <- new.env()
eval(parse(text = 'as.formula <- disable.system.call'), envir = e)


# (1) trace custom 'as.formula()' in environment e
trace(e$as.formula)


# Calling model.frame() **does not** call the the custom as.formula()
eval(parse(text = 'model.frame(\'1 ~ system("ls -la")\')'), envir = e)
#   1 system("ls -la")
# 1 1              127

# (2) trace stats:::as.formula()
trace(stats:::as.formula)

# Calling model.frame() **does** call stats:::as.formula()
eval(parse(text = 'model.frame(\'1 ~ system("ls -la")\')'), envir = e)
# trace: as.formula
#   1 system("ls -la")
# 1 1              127

编辑:FWIW,在第一个示例中 lm lm() ) 调用您的自定义model.frame()的原因lm()采用有时称为“非标准评估”的方法。 (有关该主题的更多信息, 请参阅此 pdf ,这比您可能想要的要多。)关键是lm()实际上指示model.frame()在调用环境中进行评估; 在您的情况下,这导致它找到您的 function 版本。

lm()使用非标准评估的原因是model.frame()可以访问公式中命名的变量,即使它们在调用环境中找到(而不是仅仅能够访问通过data参数传入的变量到lm() )。 正如 Thomas Lumley 在链接的 pdf 中所说:

如果要求公式中的变量在数据参数中,生活会简单得多,但在引入公式时并没有提出这一要求。

如果您有兴趣,这里是lm定义中的相关行:

mf <- match.call(expand.dots = FALSE)
...
mf[[1L]] <- as.name("model.frame")
mf <- eval(mf, parent.frame())

如果您不希望人们能够使用system ,那么覆盖定义会更容易。

assignInNamespace(
  "system", 
  function(...) stop("system calls are not allowed"), 
  getNamespace("base")
)

system("pwd")  #throws an error

我在疯狂猜测您的用例,但您是否允许用户将任意 R 代码传递给其他应用程序? 在这种情况下,您可能想要编译您自己的 R 版本,删除危险函数或用假人替换。


调用 function 时执行自定义代码的另一种可能性是trace 例如,

trace(system, quote(stop("You have called system")))  #you may also want print = FALSE

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM