我正在阅读Hadley的Advanced R并尝试一些东西。我试图创建一个lazy
闭包函数,该函数在其环境中使用提供的data.frame
返回一个函数,并使用with
并稍后能够提供其他函数参数。
lazy <- function(dataframe, x) {
function(FUN, x, ...) {
with(dataframe, FUN(x = x, ...))
}
}
lz_factory <- lazy(mtcars, "mpg")
lz_factory(mean)
lz_factory(cor, y="hp")
所以我期待数据帧是函数环境的一部分(browser
看看确认)。但是,变量名x
不供应,当我使用y
作为第一个cor
参数时,我无法提供新变量FUN
。这与为使用non-standard evaluation (NSE)的函数(with
)提供字符有关。我希望哈德利叔叔为我感到骄傲,但我用eval
,parse
,substitute
翻找错误。这意味着我不能完全了解R如何处理事情。我知道为什么它不起作用(NSE)但不知道如何使它起作用。这是错误:
> lz_factory(mean)
Error in FUN(x = x, ...) : argument "x" is missing, with no default
> lz_factory(cor, y="hp")
Error in is.data.frame(x) : argument "x" is missing, with no default
我认为我可以使用替补来解决它,因为Hadley用xyplot
显示在这里,但这也是一个失败,如下所示:
lazy <- function(dataframe, default) {
function(FUN, x, ...) {
if (missing(x)) x <- default
eval(substitute(with(dataframe, FUN(x, ...))))
}
}
lz_factory <- lazy(mtcars, "mpg")
lz_factory(mean)
lz_factory(cor, y="hp")
> lz_factory(mean)
[1] NA
Warning message:
In mean.default("mpg") : argument is not numeric or logical: returning NA
> lz_factory(cor, y="hp")
Error in cor("mpg", y = "hp") : 'x' must be numeric
那么我怎样才能使这个惰性函数工作呢:
lz_factory
最理想的是,我希望此功能可以与with
一起使用。如果那不可能,那么知道原因会很好。最后,如果不能使用with
,我该如何使该功能运作?
答案 0 :(得分:5)
这是@ MrFlick功能的略微简化版本:
lazy <- function(df, x_var = NULL) {
x <- substitute(x_var)
function(FUN, ...) {
call <- substitute(FUN(...))
if (is.null(call$x) && !is.null(x)) {
call$x <- x
}
eval(call, df, parent.frame())
}
}
关键是更好地利用substitute()
的功能,并通过直接使用eval来避免with()
。
答案 1 :(得分:4)
这个功能怎么样
lazy <- function(dataframe, ...) {
pdots <- substitute(list(...))
if(is.null(names(pdots)) || names(pdots)[1]=="") {
names(pdots)[2]<-"x"
}
function(FUN, ...) {
dots <- substitute(list(...))[-1]
if (is.null(dots$x)) {
dots$x <- pdots$x
}
with(dataframe, do.call(FUN, as.list(dots)))
}
}
这允许您在没有引号的mtcars中使用变量的名称。例如
lz_factory <- lazy(mtcars, mpg)
lz_factory(mean)
# [1] 20.09062
lz_factory(mean, x=hp)
# [1] 146.6875
lz_factory(cor, y=hp)
# [1] -0.7761684
这里我们使用额外的替换来确保我们得到延迟评估并允许您使用不带引号的变量名称。 with
将负责评估表达式。我猜可能有办法简化这个,但至少看起来有效。