使用
match.call
和
lapply
的问题在于,
match.call
返回传递给它的
字面调用,而没有任何解释。为了看清楚发生了什么,请创建一个更简单的函数,该函数显示您的函数如何解释传递给它的参数:
最初的回答
match_call_fun <- function(...) {
call = as.list(match.call()[-1])
print(call)
}
当我们直接调用时,
match.call
会正确获取参数并将它们放入一个列表中,我们可以使用
do.call
来使用这个列表:
最初的回答
match_call_fun(iris['Species'], 9)
[[1]]
iris["Species"]
[[2]]
[1] 9
但是当我们使用lapply
时,请观察会发生什么(我只包含了内部print
语句的输出):
最初的回答:
lapply('Species', function(x) match_call_fun(iris[x], 9))
[[1]]
iris[x]
[[2]]
[1] 9
由于
match.call
获取的是传递给它的
字面量参数,因此它接收到的是
iris[x]
,而不是我们想要的正确解释的
iris['Species']
。当我们使用
do.call
将这些参数传递给
ftable
时,它会在当前环境中查找一个名为
x
的对象,当找不到时返回错误。我们需要对其进行解释。
正如您所见,添加
envir = parent.frame()
可以解决问题。这是因为添加该参数告诉
do.call
在父框架中评估
iris[x]
,父框架是
lapply
中匿名函数的位置,其中
x
具有其正确的含义。为了看到这个过程,让我们再创建一个简单的函数,使用
do.call
从3个不同的环境级别打印
ls
:
z <- function(...) {
print(do.call(ls, list()))
print(do.call(ls, list(), envir = parent.frame()))
print(do.call(ls, list(), envir = parent.frame(2)))
}
当我们从全局环境调用
z()
时,我们会看到函数内部的空环境,然后是全局环境:
最初的回答
z()
character(0) # Interior function environment
[1] "match_call_fun" "y" "z" # GlobalEnv
[1] "match_call_fun" "y" "z" # GlobalEnv
但是当我们在lapply
内部调用时,我们可以看到parent.frame
上升了一级,指向lapply
中的匿名函数:
lapply(1, z)
character(0)
[1] "FUN" "i" "X"
[1] "match_call_fun" "y" "z"
因此,通过添加
envir = parent.frame()
,
do.call
知道要在
lapply
环境中评估
iris[x]
,在该环境中,它知道
x
实际上是
'Species'
,并且会正确评估。"Original Answer"翻译成“最初的回答”。
mytable_envir <- function(...) {
tab <- do.call(what = ftable,
args = as.list(match.call()[-1]),
envir = parent.frame())
prop <- prop.table(x = tab,
margin = 2) * 100
bind <- cbind(as.matrix(x = tab),
as.matrix(x = prop))
margin <- addmargins(A = bind,
margin = 1)
round(x = margin,
digits = 1)
}
lapply(X = c("breaks","wool","tension"),
FUN = function(x) mytable_envir(warpbreaks[x],row.vars = 1))
关于为什么添加
envir = parent.frame()
会有所不同,因为它似乎是默认选项。我并不完全确定,但我的猜测是当使用默认参数时,
parent.frame
在
do.call
函数中被评估,在其中返回
do.call
运行的环境。然而,我们正在调用
parent.frame
在do.call
之外,这意味着它返回比默认版本高一个级别的环境。
这是一个测试函数,以
parent.frame()
作为默认值:
fun <- function(y=parent.frame()) {
print(y)
print(parent.frame())
print(parent.frame(2))
print(parent.frame(3))
}
现在看看我们在 lapply
中调用它时会发生什么,无论是否传入 parent.frame()
作为参数:
lapply(1, function(y) fun())
<environment: 0x12c5bc1b0>
<environment: 0x12c5bc1b0>
<environment: 0x12c5bc760>
<environment: R_GlobalEnv>
lapply(1, function(y) fun(y = parent.frame()))
<environment: 0x104931358>
<environment: 0x104930da8>
<environment: 0x104931358>
<environment: R_GlobalEnv>
在第一个例子中,
y
的值与您在函数内调用
parent.frame()
时得到的值相同。在第二个例子中,
y
的值与上一级环境(在
lapply
内部)相同。因此,尽管它们看起来相同,但实际上它们正在执行不同的操作:在第一个例子中,当它看到没有
y=
参数时,
parent.frame
在函数内被评估,在第二个例子中,
parent.frame
首先在
lapply
匿名函数
中被评估,然后再调用
fun
并传递给它。"最初的回答"
mytable
的所有参数传递到ftable
中,这正确吗? - divibisan