在调用堆栈中显示特殊的基本函数

时间:2012-10-24 21:29:30

标签: r callstack

This question提示以下问题:有没有办法查看调用堆栈中的special primitive函数?

例如,创建一个在退出时返回调用堆栈的函数:

myFun <- function(obj){
  on.exit(print(sys.calls()))
  return(obj)
}

调用此函数并使用assign将其结果分配给对象可避免使用特殊的原始函数:

> assign("myObj",myFun(4))
[[1]]
assign("myObj", myFun(4))

[[2]]
myFun(4)

但是使用赋值运算符,它会被排除在堆栈之外

> `<-`(myObj, myFun(6))
[[1]]
myFun(6)

当然,想要在调用堆栈中看到赋值运算符可能并不常见,但replog等其他函数也会被隐藏

2 个答案:

答案 0 :(得分:6)

我认为没有办法通过调用堆栈访问对原始函数的调用。这就是原因。

评估“典型”R函数时:

  1. 提供的参数与正式参数匹配。
  2. 创建一个新环境(带有指向其封闭环境的指针),并将正式参数分配给它。
  3. 在新创建的环境中评估函数体。
  4. 当函数调用彼此嵌套时构建的封闭环境链是“调用堆栈”或“帧堆栈”,sys.calls()sys.frames()等提供了一些访问权限

    我强烈怀疑对原始函数的调用没有出现在调用堆栈上,因为在评估期间没有创建R端环境。没有创建环境,因此调用堆栈上不会出现任何环境。

    有关更多见解,请参阅约翰·钱伯斯如何描述Software for Data Analysis第464页对原始函数的评估:

      

    对这些功能之一的调用的评估以通常的方式开始,   但是当评估者发现函数对象是基元时   它不是R中定义的函数,而是分支到完全不同的函数   计算。该对象似乎只是一个带有形式的函数对象   参数和函数.Primitive()的调用,带有字符串参数。   实际上,它基本上只包含一个表的索引   实现R核心的C代码。表的条目标识了一个   核心中的C例程负责评估对此特定的调用   原始。评估者将控制转移到该例程,并期望   将C语言指针返回到表示的R对象的例程   电话的价值。

答案 1 :(得分:1)

我不认为乔希的回答是正确的。

好吧,如果<-在您的示例中的调用堆栈上, 将是正确的。 但它不是

小概述:正常的R函数评估将参数视为 promises ,在访问时会被懒惰地评估。这意味着在以下调用中:

foo(bar(baz))

bar(baz)foo内评估(如果有的话)。因此,如果我们检查bar内的调用堆栈,就像这样:

bar = function (x) {
    sys.calls()
}

...然后它看起来如下:

[[1]]
foo(bar(baz))

[[2]]
bar(baz)

唉,正如您所指出的,<-(和=)不是正常函数,它是一个原始函数(BUILTINSXP)。事实上,它是defined in the R source如下:

{"<-",      do_set,     1,  100,    -1, {PP_ASSIGN,  PREC_LEFT,   1}},

看看第四个论点:100。此代码之前的注释解释了数字的含义。这是相关部分,解释最左边的数字:

  

Z = 1表示在调用(BUILTINSXP

之前评估参数

这意味着在分配之前,对bar(baz)的调用进行了以下代码评估

`<-`(x, bar(baz))

这就是为什么<-未显示在sys.calls()列表中的原因:不是当前通话。它在bar完成评估后被调用。

有一种方法可以解决此限制:您可以在R代码中重新定义<- / =。如果你这样做,它的行为就像一个普通的R函数:

`<-` = function (lhs, rhs) {
    name = as.name(deparse(substitute(lhs), backtick = true))
    rhs # evaluate expression before passing it to `bquote`, for a cleaner call stack
    eval.parent(bquote(base::`<-`(.(name), .(rhs))))
}

但是,请注意,在重新定义<-的范围内,每个后续分配都会产生不可忽视的性能影响:实际上,它会使分配大约为1000( !!!) 慢点。这通常是不可接受的。