将 function() 应用于 list() 和 new.env() 之间的区别?

Difference between applying a function() to list() and to new.env()?

为什么我打印 x$val 会得到两个不同的结果?我知道第一个是列表,第二个是环境,但我不明白是什么导致第二个块的 x$val 结果 = NA

x <- list()
x$val <- 1
myfun <- function(x) {x$val <- x$val + NA} 
myfun(x)
x$val
##[1] 1
x <- new.env()
x$val <- 18
myfun <- function(x) {x$val <- x$val + NA} 
myfun(x)
x$val
##[1] NA

环境是 R 中的“引用对象”。这意味着如果您将一个变量分配给一个新变量,则对任一副本的更改都会影响两个副本。列表与大多数其他对象一样,并在分配时被复制。

因此,在您的第一个示例中,myfun(x) 制作了列表 x 的单独副本,并在函数中对其进行处理。对全局变量x.

没有影响

在您的第二个示例中,myfun(x) 对环境 x 进行了新引用,并在函数中对其进行处理。这也会影响原始变量。

好的,基于使用 <<- 一些示例的风险的合法评论。

有风险的解决方案

x <- list("val" = 1L)
myfun <- function(x) {x$val <<- x$val + NA} 
myfun(x)
x
# $val
# [1] NA

有风险的解决方案大错特错

x <- list("val" = "Please do not alter me at all")
y <- list("val" = 1L)
myfun <- function(x) {x$val <<- x$val + NA} 
myfun(y)
x
# $val
# [1] NA
y
# $val
# [1] 1

让你的函数输出你的数据

x <- list("val" = 1L)
myfun <- function(x) list("val" = x$val + NA)
x <- myfun(x)
x
# $val
# [1] NA

使用环境

y  <- new.env()
y$val <- 18L
myfun <- function(x) {x$val <- x$val + NA} 
myfun(y)
y$val
# [1] NA

这里有几个问题:

  1. Return value 函数 returns 最后执行的语句的值,在本例中是 [= 的两个实例12=] return x$valNA (将 NA 添加到任何数字得到 NA)所以它们 return 相同的值。

  2. 修改时复制 如果在函数中修改了诸如 x 的对象,则该函数会创建该对象的副本,然后修改复制。函数外的原始对象没有改变。

  3. 对象标识环境的标识独立于它们的内容,因此改变环境的内容不会改变环境本身的标识——它只是更改内容。因此,更改环境的内容不会导致在函数内复制环境。 (这类似于 C 中的指针,其中程序可以修改指向的数据而不修改指针本身。)另一方面,列表没有与其内容不同的标识。在函数中修改列表的内容会导致将列表复制到新列表,然后修改新列表。

例子

下面,我们使用pryr中的address来跟踪列表的地址。对于环境,简单地打印环境将显示其地址,因此我们不需要它。下面的跟踪语句导致 R 在进入和退出时显示地址。

列表的地址在进入函数之前和进入时是...968,但在函数内修改它之后,它已成为位于新地址...200 的新列表,这是函数的本地地址,并且与仍在地址 ...968 的函数外部列表不同。

library(pryr)

x <- list()
x$val <- 1
myfun_env <- function(x) {x$val <- x$val + NA} 
trace(myfun_list, tracer = quote(print(address(x))), exit = quote(print(address(x))))
## [1] "myfun_list"
address(x)
## [1] "000000000bbbb968"
myfun_list(x)
## Tracing myfun_list(x) on entry 
## [1] "000000000bbbb968"
## Tracing myfun_list(x) on exit 
## [1] "000000000b368200"
## [1] NA
address(x)
## [1] "000000000bbbb968"

另一方面,在环境的情况下,它具有与其内容不同的身份,因此更改内容不会导致将环境复制到新环境。环境从 ...238 开始,并且在整个代码中永远不会改变。

x <- new.env()
x$val <- 18
myfun_env <- function(x) {x$val <- x$val + NA} 
trace(myfun_env, tracer = quote(print(x)), exit = quote(print(x)))
## [1] "myfun_env"
x
## <environment: 0x000000000cac4238>
myfun_env(x)
## Tracing myfun_env(x) on entry 
## <environment: 0x000000000cac4238>
## Tracing myfun_env(x) on exit 
## <environment: 0x000000000cac4238>
x
## <environment: 0x000000000cac4238>