下面是我的代码和结果。 第一次尝试会引发错误,第二次尝试会返回结果而不引发错误。 我对这个过程很好奇。
tmp_func <- function(x = 1)
{
x <- x+1
if(!is.null(y=NULL))
{
}
return(x)
}
c <- tmp_func()
# Error in is.null(y = NULL) :
# supplied argument name 'y' doe not match 'x'
c <- tmp_func()
c
#[1] 2
首先要理解的是,所引用的
x
不是函数中的x
,而是x
期望作为参数的is.null()
。函数签名是:
is.null
# function (x) .Primitive("is.null")
让我们将函数的参数重命名为
a
以使其更清晰一些(并删除不必要的加法操作):
tmp_func <- function(a = 1) {
if (!is.null(y = NULL)) {}
return(a)
}
tmp_func()
# Error in is.null(y = NULL) :
# supplied argument name 'y' does not match 'x
tmp_func()
# [1] 1
我们可以看到它仍然在抱怨
x
,而不是a
。原因是 R 有一个即时 (JIT) 编译器,它将常用函数编译为字节码。您可以使用 compiler::enableJIT(0)
检查您的设置级别。我怀疑至少会是 2:
在第 1 级,大型函数将在首次使用之前进行编译。在第 2 级,小函数也会在第二次使用之前进行编译。 (来源)
第二次运行函数时,它会被编译。如果我们在第一次和第二次调用后打印函数源代码,我们可以看到这一点:
tmp_func <- function(a = 1) {
if (!is.null(y = NULL)) {}
return(a)
}
tmp_func()
# Error in is.null(y = NULL) :
# supplied argument name 'y' does not match 'x
tmp_func # print source
# function(a = 1) {
# if (!is.null(y = NULL)) {}
# return(a)
# }
tmp_func()
# [1] 1
tmp_func # print source again
# function(a = 1) {
# if (!is.null(y = NULL)) {}
# return(a)
# }
# <bytecode: 0x3f42e28>
注意最后一行显示该函数现在已编译为字节码。编译器似乎并不关心参数名称,大概是因为它跳过了 R 函数并直接调用
.Primitive("is.null")
,这并不关心。它只是在常量 !is.null()
上看到 NULL
,并对其进行优化。我们也可以看到实际的说明:
compiler::disassemble(tmp_func)
list(.Code, list(12L, BASEGUARD.OP, 1L, 6L, LDNULL.OP, ISNULL.OP,
NOT.OP, 4L, BRIFNOT.OP, 5L, 14L, LDNULL.OP, GOTO.OP, 15L,
LDNULL.OP, POP.OP, GETVAR.OP, 7L, RETURN.OP), list({
if (!is.null(y = NULL)) {
}
return(a)
}
这是奇怪的行为,但
is.null(y = NULL)
并不是真正应该出现的逻辑测试。无论如何,第二次得到不同结果的原因是该函数已编译。如果你想避免这种情况,你可以更改你的 JIT 编译级别:
compiler::enableJIT(0)