我正在阅读Hadley Wickham的Advanced R,试图解决第2.3.6节中有关修改时复制的exercise 2:
解释为什么运行此代码时
tracemem()
显示两个副本。提示:请仔细查看此代码与本节前面显示的代码之间的区别。x <- c(1L, 2L, 3L) tracemem(x) x[[3]] <- 4
(先前的代码将双精度列表中的一个元素更改为另一双精度,仅产生一个副本。)
在Grosser和Bumann的Advanced R Solutions they explain中,第二个副本归因于强制类型,从整数到双精度。它们没有将x
定义为c(1L, 2L, 3L)
,而是使用了x <- 1:3
,我认为这是等效的(使用identical
进行比较也返回TRUE
)。但是,运行上面的代码(最终)仅产生一个副本,但是运行下面的代码则产生两个副本:
x <- 1:3
tracemem(x)
x[[3]] <- 4
运行此代码还会产生两个副本:
x <- c(1L, 2L, 3L)
typeof(x)
tracemem(x)
x[[3]] <- 4
例如,将typeof
替换为class
仅产生一个副本,而将其替换为mode
或pryr::otype
则产生两个副本。但是只需打印出x
即可得到一份副本。
那么c(1L, 2L, 3L)
和1:3
之间有什么区别,为什么调用上面的一些但不是全部函数会改变行为呢?
我使用Rterm
在RStudio的控制台中从PowerShell运行代码时,会得到相同的行为。
答案 0 :(得分:0)
我在@brodieg的博客https://www.brodieg.com/2019/02/18/an-unofficial-reference-for-internal-inspect/#fn4中找到了答案
不使用tracemem,而是更深入地研究.Internal(inspect(x))
x <- c(1L, 6L, 10L)
.Internal(inspect(x))
#> @7fc0397e0f88 13 INTSXP g0c2 [NAM(1)] (len=3, tl=0) 1,6,10
x <- c(1L, 6L, 10L)
typeof(x)
.Internal(inspect(x))
#> @7fc0397e0f08 13 INTSXP g0c2 [NAM(7)] (len=3, tl=0) 1,6,10
.Internal(inspect(x))
的返回值是一堆信息,但重要的是NAM。 NAM是参考计数器,是一种确定有多少R对象指向同一基础事物的方法。在这里typeof()
使它递增。 typeof()
是一个闭包,闭包的参数上的“ NAM”值始终递增。
在为元素分配新值时,并非总是会复制每个@brodieg x,但是当NAM引用计数启发式提示可能存在多个对象引用时。
我找不到关于引用和:
的任何信息,但是使用它创建的对象的引用计数自动大于1
x <- 1:3
.Internal(inspect(x))
#> @7fc03bad6388 13 INTSXP g0c0 [NAM(7)] 1 : 3 (compact)
因此,对于带有x <- c(1L, 6L, 10L)
的原始情况,引用计数仅为1,R启发式方法不会触发分配的副本,但会触发类型强制。这就是tracemem
仅被触发一次的原因。