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

  • 定义函数
tmp_func <- function(x = 1)
 {
   x <- x+1
   if(!is.null(y=NULL))
   {
   }
   return(x)
 }
  • 首次try
c <- tmp_func()
#  Error in is.null(y = NULL) : 
#  supplied argument name 'y' doe not match 'x'
  • 二次try
c <- tmp_func()
c
#[1] 2

推荐答案

第二次运行此函数时,它会被R的JT编译器编译为字节码.然而,为什么这会产生这种精确的效果取决于您正在调用的函数和您正在提供的参数.

is.null() does not take a y argument

首先要理解的是,错误消息中引用的x不是函数中的x,而是is.null()期望作为参数的x.函数签名是:

is.null
# function (x)  .Primitive("is.null")

每当我们向原始函数提供它不期望的命名参数时,我们就可以出现这个特定错误:

is.null(y = NULL)
# Error in is.null(y = NULL) : 
#   supplied argument name 'y' does not match 'x'
abs(y = 1)
# Error in abs(y = 1) : supplied argument name 'y' does not match 'x'

我不完全清楚您试图用此语法做什么.如果您想将NULL分配给y,那么您需要is.null(y <- NULL),这不会产生错误.

让我们将函数的参数重命名为a,以减少x附近的任何歧义,并删除不必要的加法操作:

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有一个实时(JT)compiler,它将常用函数编译为字节码.您可以使用oldJit <- compiler::enableJIT(0)判断您的级别设置为哪个级别并判断oldJit(这会将级别设置为0,以便您稍后可能想将其更改回来).我怀疑至少是2:

在1级,大型函数将在首次使用之前进行编译.在第2级,小函数在第二次使用之前也会被编译.(source)

编译该函数会更改它处理此运算式的方式

第二次运行函数时,它将被编译.如果我们在第一次和第二次调用后打印函数源代码,我们可以看到这一点:

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函数,而且它与C代码中.Primitive("is.null")的接口方式也不关心.我们可以在docs中看到,不变的NULL参数是一种特殊情况:

常数参数按cmpConstArg编制.同样,对于常见的特殊常数NULLTRUEFALSE有特殊指令.(p13)

您可以自己测试这一点-例如,如果将其更改为is.null(y = "a"),则已编译的代码的行为与已解释的代码相同.

然而,在这种特殊情况下,编译器只看到!is.null()应用于NULL的常数.我们可以在生成的指令中看到:

compiler::disassemble(tmp_func)
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)

这是奇怪的行为,我认为从技术上讲这是一个编译器错误.但is.null(y = NULL)并不是一个真正有意义的表达,也不是我希望看到的,所以它看起来不是一个非常重要的表达.在任何情况下,如果您想避免这种情况,您可以更改您的JT compilation levels:

compiler::enableJIT(0)

R相关问答推荐

r带有参考年的两年移动平均线

即使声明引发错误,R函数也会在第二次try 时返回结果

棒棒糖图表大小和线宽参数故障标签未出现

跨列应用多个摘要函数:summerise_all:列表对象无法强制为double类型'

如何在R中正确对齐放射状图中的文本

基于现有类创建类的打印方法(即,打印tibles更长时间)

在位置周围设定一个半径并识别该半径内的其他位置

Rplotly中的Sankey Diagram:意外连接&

如何优化向量的以下条件赋值?

为了网络分析目的,将数据框转换为长格式列联表

您是否可以使用facet_rap设置一个较低的限制来对ggmap上的比例中断进行zoom ?

基于R中的间隔扩展数据集行

更改STAT_VALLES/STAT_PEAKS中的箭头线宽/大小

从多面条形图中删除可变部分

从R中发出的咕噜声中的BUG?

将具有坐标列表列的三角形转换为多个多边形

如何移动点以使它们的打印不重叠

如何调整一个facet_work()面板内的框图和移动标签之间的水平宽度?

ArrangeGrob()和类似的替代方法不接受Grob列表.在Grid.Draw,返回:glist中的错误(...):仅允许在glist";中使用Grobs;

注释不会绘制在所有ggplot2面上