R 语言定义手册为我们提供了 R 如何评估以下形式的表达式的指针:
x$foo[1] <- 15
也就是说,好像我们已经调用了
`*tmp*` <- x
x <- "$<-.data.frame"(`*tmp*`, name = "foo",
value = "[<-.data.frame"("$.data.frame"(`*tmp*`, "foo"),
1, value = 15))
rm(`*tmp*`)
如果为了说明的目的,我们放弃实际使用的方法,中间部分可能更容易解决:
x <- "$<-"(`*tmp*`, name = "foo",
value = "[<-"("$"(`*tmp*`, "foo"), 1, value = 15))
回到您使用iris 的示例,我们有类似
iris$foo[1] <- 15
这里,函数是递归计算的。首先提取器函数"$"用于从iris访问组件"foo",即NULL:
> "$"(iris, "foo")
NULL
然后,"[<-" 用于将上面返回的对象的第一个元素(NULL)替换为值15,即调用:
> "[<-"(NULL, 1, value = 15)
[1] 15
现在,这是在我们调用的最外层用作参数value 的对象,即使用"$<-" 的赋值:
> head("$<-"(iris, "foo", value = 15))
Sepal.Length Sepal.Width Petal.Length Petal.Width Species foo
1 5.1 3.5 1.4 0.2 setosa 15
2 4.9 3.0 1.4 0.2 setosa 15
3 4.7 3.2 1.3 0.2 setosa 15
4 4.6 3.1 1.5 0.2 setosa 15
5 5.0 3.6 1.4 0.2 setosa 15
6 5.4 3.9 1.7 0.4 setosa 15
(这里用head() 包裹以限制显示的行数。)
这有望解释函数如何调用进度。最后一个要处理的问题是为什么整个向量foo 设置为15?答案在?"$<-.data.frame"的详细信息部分给出:
Details:
....
Note that there is no ‘data.frame’ method for ‘$’, so ‘x$name’
uses the default method which treats ‘x’ as a list. There is a
replacement method which checks ‘value’ for the correct number of
rows, and replicates it if necessary.
关键位是最后一句话。在上面的例子中,最外层的赋值使用了value = 15。但此时,我们想要替换 整个 组件 "foo",其长度为 nrow(iris)。因此,实际使用的是value = rep(15, nrow(iris)),在最外层的赋值/函数调用中。
这个例子更加复杂,因为你必须从方便表示法转换
x$foo[1] <- 15
使用"$<-"()、"[<-"() 和"$"() 进行正确的函数调用。 R 语言定义的Section 3.4.4 中的示例使用了这个更简单的示例:
names(x)[3] <- "Three"
计算结果为
`*tmp*` <- x
x <- "names<-"(`*tmp*`, value="[<-"(names(`*tmp*`), 3, value="Three"))
rm(`*tmp*`)
这更容易理解,因为names() 看起来像一个普通的函数调用。