Руководство по определению языка 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
Здесь функции оцениваются рекурсивно. Сначала функция извлечения "$"
используется для доступа к компоненту "foo"
из iris
, то есть 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
в правильные вызовы функций с использованием "$<-"()
, "[<-"()
и "$"()
. Пример в разделе 3.4.4 определения языка R использует этот более простой пример:
names(x)[3] <- "Three"
, что оценивается как
`*tmp*` <- x
x <- "names<-"(`*tmp*`, value="[<-"(names(`*tmp*`), 3, value="Three"))
rm(`*tmp*`)
, что немного легче разобраться, потому что names()
выглядит как обычный вызов функции.