Ответ 1
Функция '[<-'()
(традиционно) используется для переназначения и, в более широком смысле, является функцией замены. Он также является общим (более конкретно, внутренним родовым), который позволяет вам писать собственные методы для него, как вы правильно поняли.
Запасные функции
В общем, когда вы вызываете функцию замены, например...
foo(x) <- bar(y)
... выражение в правой части <-
(поэтому здесь bar(y)
) передается как аргумент именованного value
в 'foo<-'()
с x
в качестве первого аргумента, а объект x
переназначается с помощью результат: т.е. указанный вызов эквивалентен написанию:
x <- 'foo<-'(x, value = bar(y))
Поэтому для того, чтобы работать вообще, все функции замены должны принимать как минимум два аргумента, один из которых должен иметь именованное value
. Большинство функций замены имеют только эти два аргумента, но есть и исключения: например 'attr<-'
и, как правило, subassignment.
Subassignment
Когда у вас есть вызов subassignment, такой как x[i, j] <- y
, i
и j
передаются как дополнительные аргументы функции '[<-'()
с x
и y
в качестве первого и аргументов value
соответственно:
x <- '[<-'(x, i, j, value = y) # x[i, j] <- y
В случае matrix
или data.frame
, i
и j
будут использоваться для выбора строк и столбцов; но в целом это не обязательно. Метод для пользовательского класса мог бы что-либо сделать с аргументами. Рассмотрим этот пример:
x <- matrix(1:9, 3, 3)
class(x) <- "newclass"
'[<-.newclass' <- function(x, y, z, value) {
x + (y - z) * value # absolute nonsense
}
x[1, 2] <- 9
x
#> [,1] [,2] [,3]
#> [1,] -8 -5 -2
#> [2,] -7 -4 -1
#> [3,] -6 -3 0
#> attr(,"class")
#> [1] "newclass"
Является ли это полезным или разумным? Возможно нет. Но действительно ли это R-код? Абсолютно!
Менее распространено видеть пользовательские методы переназначения в реальных приложениях, поскольку '[<-'()
обычно "просто работает", как вы могли ожидать, на основе базового объекта вашего класса. Заметным исключением является '[<-.data.frame'
, где базовый объект является списком, но subassignment ведет себя как матричный. (С другой стороны, многим классам нужен собственный метод подмножества, так как метод по умолчанию '['()
уменьшает большинство атрибутов, включая атрибут class
, см. ?'['
Для деталей).
Что касается того, почему ваш пример не работает: помните, что вы пишете метод для общей функции, и применяются все обычные правила. Если мы используем функциональную форму '[<-'()
и расширяем отправку метода в вашем примере, мы сразу видим, почему это не удается:
'[<-.newclass' <- function(x, i, j, value) {
x <- '[<-.newclass'(x, i, j, value = value^2) # x[i, j] <- value^2
}
То есть, функция была определена рекурсивно, без базового случая, что привело к бесконечному циклу. Один из способов обойти это можно было бы unclass(x)
до вызова следующего метода:
'[<-.newclass' <- function(x, i, j, value) {
x <- unclass(x)
x[i, j] <- value^2
x # typically you would also add the class back here
}
(Или, используя несколько более продвинутую технику, тело можно также заменить следующим явным следующим методом: NextMethod(value = value^2)
. Это играет лучше с наследованием и суперклассами.)
И просто чтобы убедиться, что он работает:
x <- matrix(1:9, 3, 3)
class(x) <- "newclass"
x[1, 2] <- 9
x
#> [,1] [,2] [,3]
#> [1,] 1 81 7
#> [2,] 2 5 8
#> [3,] 3 6 9
Совершенно смущает!
Что касается контекста примера подавления Dowle "ничего не делать", я считаю, что это должно было проиллюстрировать это в R 2.13.0, пользовательский метод переназначения всегда будет вызывать глубокую копию объекта, даже если сам метод ничего. (Это уже не так, поскольку я считаю, что 3.1.0.)
Создано в 2018-08-15 пакетом reprex (v0.2.0).