当您使用 R 连接两个向量时c(),它“组合参数并产生向量”。它是否通过创建一个新向量来获取两个向量的元素来组合它们,或者是否有一种方法可以按字面意思组合为两个向量分配的数据空间?
当我搜索时,我找不到解释。所有的视觉表示c()实际上只是将第二个向量附加到第一个向量的末尾,但我认为这只是为了让我们可以轻松地理解这个函数的作用,而不是实际发生的情况。
All*_*ron 29
当您调用 时c(),将分配一个新向量,并将现有向量组合到其中。它发生在底层 C 代码中。
PROTECT(ans = allocVector(mode, data.ans_length));
Run Code Online (Sandbox Code Playgroud)
这可能看起来很浪费,因为我们已经将值写入内存,那么为什么不直接包装几个指向该内存的指针并将其称为向量呢?
有几个原因。
首先,R 对向量执行的许多算术和统计运算都是通过迭代连续内存中的元素来完成的。如果元素不在连续内存中,这是不可能的。将会有很多地址检查步骤和内存地址之间的跳转,这会让事情变得更慢。在 R 之外,C 或 C++ 中的连接向量也是通过分配新向量来完成的,原因大致相同。
第二个原因是避免碎片和内存泄漏。如果我们通过连接其他向量的子集创建一个向量而不分配专用内存,那么我们最终会得到一堆指向内存空闲存储中不同位置的指针。如果我们随后使用该向量的子集,我们将面临一场噩梦:内存指针指向向量片段的内存指针,以及垃圾收集器无法重新使用或回收的未使用的向量片段块。
第三个原因是 R 用户期望修改时复制行为。例如,如果我们有:
a <- c(1, 2, 3)
b <- c(a, a)
b
#> [1] 1 2 3 1 2 3
Run Code Online (Sandbox Code Playgroud)
然后我们期望能够更改单个元素:
b[6] <- 6
b
#> [1] 1 2 3 1 2 6
Run Code Online (Sandbox Code Playgroud)
然而,如果b没有分配自己的数据,则此操作将更改第三个元素b以及第六个元素。
正如 Nicola 在评论中指出的,另一个原因是c会进行类型检查和类型之间的隐式转换,以确保新向量的底层存储模式一致。这允许整数、双精度数、逻辑向量、因子和字符串之间存在一些简单且定义明确的灵活性,如果创建的向量由c现有向量的片段组成,则这是不可能的。
从概念上讲,R 中的内存分配是这样工作的:每个 R 对象都作为一个SEXP对象存储在 C 中。这是一个基本上是指向数据本身的指针的结构,它作为称为 的结构存储在内存中SEXPREC。
因此,如果我们运行代码:
A <- 1:4
B <- 5:14
Run Code Online (Sandbox Code Playgroud)
向量A可能B像这样存储在内存中:
如果我们那么做
C <- c(A, B)
Run Code Online (Sandbox Code Playgroud)
然后在内存中我们得到:
所指向的 SEXPREC 中的数据是从和C所指向的其他两个 SEXPREC 对象中的数据复制而来的AB
| 归档时间: |
|
| 查看次数: |
363 次 |
| 最近记录: |