这里有几个问题:
返回值函数返回执行的最后一条语句的值,在这种情况下,myfun
返回的两个实例x$val
都是NA
(将 NA 添加到任何数字都会得到 NA),因此它们确实返回相同的值。
修改时复制如果在函数中修改了某个对象,例如,x
该函数会创建该对象的副本,然后修改该副本。函数外的原始对象没有改变。
对象身份环境具有独立于其内容的身份,因此更改环境的内容不会更改环境本身的身份——它只会更改内容。因此,更改环境的内容不会导致在函数内复制环境。(这类似于 C 中的指针,其中程序可以修改指向的数据而不修改指针本身。)另一方面,列表没有与其内容不同的标识。在函数中修改列表的内容会导致列表被复制到新列表,然后新列表被修改。
例子
下面,我们使用address
from 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>