[英]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
就像lm
在model.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.