我正在尝试传递一个应该根据数据框进行评估的表达式,本质上是镜像
subset()
。问题是,我需要能够将它放在其他函数中。
这本质上意味着保留表达式并且在合适的时间之前不对其进行求值(即调用类似子集的函数)。
我最终成功的方法是传递
env
和默认值 sys.frame(sys.nframe())
,它应该评估函数的框架。缺点是这需要作为每个外部函数的参数以保留嵌套属性:
f1 = function(x, expr, env=sys.frame(sys.nframe())){
expr = substitute(expr, env)
eval(expr, x)
}
f2 = function(x, expr, env=sys.frame(sys.nframe())){
f1(x, expr, env)
}
f3 = function(x, expr, env=sys.frame(sys.nframe())){
f2(x, expr, env)
}
f4 = function(x, expr){
f2(x, expr) # no env passing
}
m = head(mtcars, 2) # less typing
f1(m, mpg > 20) # TRUE TRUE -- expected
f2(m, mpg > 20) # TRUE TRUE -- yaay
f3(m, mpg > 20) # TRUE TRUE -- yaay
f4(m, mpg > 20) # expr -- naay :(
我可以重写
f1
以便它自动展开调用堆栈并找到正确的 env
来替换传递的表达式吗?
您可以使用
do.call()
建立通话。例如
f1 = function(x, expr){
expr = substitute(expr)
eval(expr, x)
}
f2 = function(x, expr){
do.call("f1", list(x, substitute(expr)))
}
f3 = function(x, expr){
do.call("f2", list(x, substitute(expr)))
}
f1(m, mpg > 20)
# [1] TRUE TRUE
f2(m, mpg > 20)
# [1] TRUE TRUE
f3(m, mpg > 20)
# [1] TRUE TRUE
但总的来说,我强烈建议不要这样做。通常,您将有一个面向用户的函数来处理非标准评估,然后您只需传递一个带引号的语言字符串,或者在 rlang/tidyverse 世界中,传递一个您可以稍后评估的 quosure。当尝试以编程方式使用函数时,依赖嵌套的非标准评估调用通常只会导致更多麻烦。