【问题标题】:Function `[<-` will _replace_ an element, but not append an _element_函数 `[<-` 将_replace_一个元素,但不附加一个_element_
【发布时间】:2013-02-17 04:19:54
【问题描述】:

我在使用'[&lt;-' 时注意到以下内容。我在 replace 元素方面很成功,但在 appending 元素到向量中却没有成功。

例子:

VarX <- integer()
VarX[1] <- 11
`[<-`(VarX, 2, 22)
VarX
# [1] 11

# Expected the value of VarX to be:  
# [1] 11 22

# Also tried: 
`[<-`(VarX, i=2, value=22)
VarX 
# [1] 11

但是,如果索引处已经有一个值,则该值会被替换。

VarX <- integer()
VarX[1] <- 11
VarX[2] <- 99
VarX
# [1] 11 99
`[<-`(VarX, 2, 22)
VarX
# [1] 11 22

我只是语法错误,还是按预期进行?任何进一步的见解 这里发生的事情将不胜感激。

请注意,除了更好地理解语言之外,这里没有具体的目标。

关于 @Roland 和 @Dason 的 cmets 的更新。

似乎该行为与最初分配对象值的方式有关。例如,当分配给VarX 的值为1:2c(1, 2) 时,[(VarX, 2, 22) 的行为会给出不同的结果,如下所示:

### 更改未保存到 VarX

rm(VarX)  # actually ran:     rm(list=ls(all=TRUE))
VarX <- 1:2
VarX
# [1] 1 2

`[<-`(VarX, 2, 22)
# [1]  1 22

VarX
# [1] 1 2

### 更改已保存到 VarX

rm(VarX)  # actually ran:     rm(list=ls(all=TRUE))
VarX <- c(1, 2)
VarX[2] <- 2
VarX
# [1] 1 2

`[<-`(VarX, 2, 22)
# [1]  1 22

VarX
# [1]  1 22



> sessionInfo()
R version 2.15.1 (2012-06-22)
Platform: x86_64-apple-darwin9.8.0/x86_64 (64-bit)

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base  

【问题讨论】:

  • 这很好用:VarX &lt;- integer(); VarX[1] &lt;- 11; VarX[2] &lt;- 22。为什么要尝试以非标准方式调用该函数?
  • 我得到不同的行为(替换和附加基本相同)。 ` [&lt;- ` (VarX, 2, 22) 打印 [1] 11 22,但 VarX 之后分别给出 [1] 11[1] 11 99
  • 您可以在干净的会话中重新运行您的代码部分吗?我找不到[&lt;- 实际修改第一个参数的情况。如果它被修改但不执行实际分配,它会返回第一个参数的值。
  • 我刚开始一个干净的会话,现在和你一样。似乎某些包修改了原语。
  • @JoshuaUlrich 为什么不呢?我认为,如果您尝试以某种方式“破坏”该语言并了解您所做的事情背后的方式和原因,那么您将成为一个更好的程序员!我觉得这很有趣。

标签: r variable-assignment


【解决方案1】:

函数'[&lt;-' 可能不会替换其第一个参数中的任何内容。在某些情况下,它会复制对象并对其进行修改。

参见语言定义的第 3.4.4 节:

x[3:5] <- 13:15

The result of this commands is as if the following had been executed

‘*tmp*‘ <- x
x <- "[<-"(‘*tmp*‘, 3:5, value=13:15)
rm(‘*tmp*‘)

如果必须修改x 的结构,这基本上就是将运行的内容。但是,根据 OP(和其他人,包括我自己)的实验,很明显 "[&lt;-" 函数可以就地修改元素。显然,如果要替换整个对象,则无法就地执行任何操作。

就地替换:

> x <- 1:2
> class(x)
[1] "integer"
> `[<-`(x, 2, 99L)
[1]  1 99
> x
[1]  1 99

替换整个对象,因为类型已经改变(在C函数SubAssignTypeFix中):

> x <- 1:2
> class(x)
[1] "integer"
> x[2] <- 99
> class(x)
[1] "numeric"

对象被替换的另一种情况是对被修改对象的引用不止一个:

x <- 1:2
y <- x
`[<-`(x, 2, 99L)
## [1]  1 99
x
## [1] 1 2

在调试器下运行R显示,通过x[2] &lt;- 99间接调用的赋值函数调用了C函数do_set,而直接按名称调用赋值函数时,并没有调用这个函数。

do_set 调用一个函数defineVar 来修改相应的环境。在就地替换的情况下,对象会在环境中替换自身,这正是通过名称调用赋值函数导致对象被修改(未获取副本)的确切情况。

有趣的花絮(见这里:R object identity

#### R console:
x <- 1:2
.Internal(inspect(x))
## @26b27a8 13 INTSXP g0c1 [NAM(1)] (len=2, tl=0) 1,2
x[2] <- 99

#### gdb:
Breakpoint 7, do_set (call=0x2773640, op=0x169e668, args=0x2773870, rho=0x16c6b68) at eval.c:1732   
(gdb) p s
## $135 = (SEXP) 0x192bee0
c

#### R console:
.Internal(inspect(x))
## @192bee0 14 REALSXP g0c2 [NAM(1)] (len=2, tl=0) 1,99

直接回答原问题,当[&lt;-放大向量时,复制一份。来自函数EnlargeVectorsubassign.c:113

PROTECT(newx = allocVector(TYPEOF(x), newlen));

/* Copy the elements into place. */
...

这是 R 2.15.2,我从源代码构建,没有优化和调试信息。不优化就很慢。

【讨论】:

  • 感谢@Matthew,但是为什么会根据分配给原始 var 的内容出现差异?
  • @RicardoSaporta -- b/c 当它被用作替换运算符时,[&lt;- 实际上制作整个对象的新副本,而当附加。它首先通过引用传递/修改,然后通过值传递/修改。 (不确定这是否是正确的术语,所以请任何人告诉我是否不是。)
  • @JoshO'Brien 我认为他指的是更新问题中最后两个代码块的差异。
  • @Dason -- 哦。我知道了。这是因为当您尝试将 22(numeric)分配给 integer 向量的元素时,您确实强制 R 复制该向量,将其模式转换为numeric。在第一个块中,这个工作,"[&lt;-"(VarX, 2, 22L),因为22L 与它被分配到的向量的模式相同。 简答:比较typeof(1:2); typeof(c(1,2)); typeof(22)
  • @JoshO'Brien 我认为您应该将其发布为答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多