【问题标题】:R: Where are formals for a function stored in memory?R:函数的形式在哪里存储在内存中?
【发布时间】:2018-10-09 16:31:52
【问题描述】:

当一个函数已经定义但还没有被调用时,没有默认值的形式是否存在?如果存在,是存在于执行环境中,还是存在于函数定义所在的环境中,还是其他地方?

如果一个函数已定义但尚未调用,并且已为形式分配了默认值,那么该值是否存在?如果有,它存在于什么环境中?如果默认表达式的计算结果为常量,是否已将形式分配给该值,如果提供了值,则在调用函数时将其覆盖?如果不是,那个(固定的)默认值位于定义时刻和函数调用时刻之间,在什么环境下?

在调用函数并将实际值或默认值分配给形式后,传递到主体中,并在必要时进行范围和/或评估,形式是否继续存在?如果有,那么它们存在于什么环境中?

【问题讨论】:

  • 我不知道你在问什么
  • 对我来说,问题在问什么似乎很清楚。本质上:如果你有类似fun <- function(x=2) 的东西x 是否存在于某种环境中?问题是关于那种变体。
  • AFAIK,它不存在。
  • @HongOoi - Andrew 的问题在 John Chambers 引用的上下文中是有道理的:“要理解 R 中的计算,有两个口号很有帮助:1) 存在的一切都是对象,2)发生是一个函数调用。”如果 R 中的一切都是对象,那么函数的形式也必须是对象。因此,询问有关它们所在环境的问题是合理的。

标签: r scope arguments function-definition


【解决方案1】:

一旦函数的实例被调用加载到内存中,函数的形式就作为对象存在于函数的环境中。在Advanced R中,Hadley Wickham 将此环境称为execution environment。可以通过pryr::address() 访问对象的内存位置。

作为示例,我将使用我之前编写的代码的修改版本来说明coursera.org 上约翰霍普金斯大学R Programming 课程的第二个编程作业中makeVector() 函数中的内存位置。

makeVector <- function(x = 200) {
     library(pryr)
     message(paste("Address of x argument is:",address(x)))
     message(paste("Number of references to x is:",refs(x)))
     m <- NULL
     set <- function(y) {
          x <<- y
          message(paste("set() address of x is:",address(x)))
          message(paste("Number of references to x is:",refs(x)))
          m <<- NULL
     }
     get <- function() x
     setmean <- function(mean) m <<- mean
     getmean <- function() m
     list(set = set, get = get,
          setmean = setmean,
          getmean = getmean)
}

如上代码,makeVector() 是一个 S3 对象,这意味着我们可以通过 getter 和 setter 访问其环境中的对象,也称为mutator methods

我们可以将makeVector()对象的实例加载到内存中,并使用以下代码查询x的地址和值。

makeVector()$get()

...结果:

> makeVector()$get()
Address of x argument is: 0x1103df4e0
Number of references to x is: 0
[1] 200
> 

正如我们从输出中看到的,x 确实有一个内存位置,但没有其他对象包含对它的引用。此外,x 被设置为长度为 1 且值为 200 的向量的默认值。

我在对Caching the Mean of a Vector in R 的回答中详细介绍了makeVector() 环境中的对象。

关于形式在内存中存在多久的问题,只要为存储被调用的函数实例而创建的环境在内存中,它们就存在。由于垃圾收集器对没有外部引用的对象进行操作,因此如果函数实例未保存到对象中,则只要函数调用将结果返回给父环境,它就有资格进行垃圾收集。

【讨论】:

  • 这个答案非常清晰,令人愉悦。但是,我仍然不确定它的一些细节。 (1)“函数加载到内存后的环境”和执行环境一样吗?还是调用或封闭环境?或者是其他东西?我一直在想象,形式和默认值是函数与外界的接口,并位于它们自己的“中间”环境中,但我确信这是错误的。几乎可以肯定。(2) x 的地址与 makeVector 的地址相同吗?和(续)
  • (3) 200 到 0x1103df4e0 的赋值是在函数定义时、调用时还是中间的某个时间发生?
  • 这是您的第一个回复:所以这就是他们所说的执行环境,是吗?对不起,如果我把它打到地上。我只是想确定我可以将我从你那里学到的东西与我认为我从 Hadley 的“Advanced R”中学到的东西联系起来。重新回复您的第二个答复:会的。我读了你的缓存平均帖子。非常好的作品。
  • @r2evans - 在阅读了 Yihui Xie 在library()require() 上的blog post 之后,我同意library()require() 更合适,尽管R 文档指出@ 987654342@ 旨在用于 R 函数中。感谢您一直问我这个问题。
  • 并不是我花几个小时在这个话题上夸夸其谈,但是......我认为require 的唯一实际用途是当你有两个代码路径时:一个是可用的包,另一个是可用的,也许是由于速度/效率,也许是增加了功能。对于library,第二条路径被硬编码为stop。 (并且if (!require(...)) install.packages() 通常不应该在生产中使用,也许只能在教程/演示中使用。)我之前引用过那篇文章,顺便说一句,它写得很好。
最近更新 更多