R 函数在第二次尝试时返回结果,即使“if 语句”引发错误

问题描述 投票:0回答:1

下面是我的代码和结果。 第一次尝试会引发错误,第二次尝试会返回结果而不引发错误。 我对这个过程很好奇。

  • 定义函数
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
r
1个回答
0
投票

首先要理解的是,所引用的

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)
© www.soinside.com 2019 - 2024. All rights reserved.