R语言函数内用evalq执行source时如何指定正确生效环境
问题核心原因
你遇到的问题本质是evalq()的工作机制和R的参数求值规则共同作用的结果:evalq()只会对它的第一个字面量实参执行 quotation(阻止即时求值)操作,如果你给evalq传的是一个变量(比如示例中的函数形参expr),它只会quote这个变量名本身,不会自动捕获变量指向的用户传入的表达式。
你调用thisfails(runScript())时,参数expr实际是一个延迟求值的promise,当evalq(expr, envir=env)执行时:
evalq首先quote了expr这个符号,而非expr指向的runScript()调用- 后续求值时触发了
expr这个promise的求值,而promise的默认求值环境是thisfails的调用环境(即全局环境) runScript()在全局环境执行,它内部的parent.frame()指向全局环境,因此aa被创建到了全局环境中,完全绕过了你指定的env。
而你直接写evalq(runScript(), envir=env)能正常工作,是因为runScript()直接写在evalq的第一个参数位置,evalq直接quote了这个调用,再放到env中执行,此时runScript()的parent.frame()就是env,aa自然被创建到env里。
你给出的works()函数正常也是同理:runScript()直接写在函数体内,它的parent.frame()就是works的执行环境,不会泄漏到全局。
解决方案
改用substitute先捕获用户传入的表达式,再用eval执行即可:
thisworks <- function(expr){ env <- new.env() # 捕获传入的表达式,不触发求值 captured_expr <- substitute(expr) # 在指定环境中执行捕获到的表达式 eval(captured_expr, envir = env) ls(envir = env) } thisworks(runScript()) # 输出:[1] "aa",不会在全局环境中创建aa变量
如果你一定要用evalq,只能把表达式直接写在evalq的第一个参数位置,不能通过参数传递:
thisworks2 <- function(){ env <- new.env() evalq(runScript(), envir = env) ls(envir = env) } thisworks2()
内容的提问来源于stack exchange,提问作者Berry Boessenkool
相关产品推荐
相关产品推荐

