1

如果我打印 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
4

3 回答 3

1

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

因此,在您的第一个示例中,myfun(x)制作 list 的单独副本x,并在函数中对其进行处理。它对全局变量没有影响x

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

于 2022-02-02T12:15:43.940 回答
1

这里有几个问题:

  1. 返回值函数返回执行的最后一条语句的值,在这种情况下,myfun返回的两个实例x$val都是NA(将 NA 添加到任何数字都会得到 NA),因此它们确实返回相同的值。

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

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

例子

下面,我们使用addressfrom pryr 来跟踪列表的地址。对于环境,只需打印环境将显示其地址,因此我们不需要它。下面的跟踪语句使 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>
于 2022-02-02T13:35:24.320 回答
0

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

有风险的解决方案

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
于 2022-02-02T12:52:51.243 回答