динамическое добавление функции в экземпляр класса r6
Я пытаюсь забыть refclasses (R5) и перейти на R6, но есть проблема с динамическим кодом. Я бы добавил новую функцию, и она работает в R5:
clsTrn <- setRefClass("clsTrn",
fields = list(x = "numeric"),
methods = list(
add_function = function(rcode) {
eval(parse(text=rcode), envir=.self)
}
)
)
cls <- clsTrn$new(x=4)
cls$x
# [1] 4
cls$add_function("predict = function(y) {return(.self$x*y)}")
cls$predict(3)
#[1] 12
Подобный код не работает для R6.
library(R6)
clsTrnR6 <- R6Class("clsTrnR6",
lock=FALSE,
public = list(
x = NA,
initialize = function(x) {
self$x <- x
},
add_function = function(rcode) {
eval(parse(text=rcode), envir=self)
}
)
)
clsR6 <- clsTrnR6$new(x=4)
clsR6$x
#[1] 4
clsR6$add_function("predict = function(y) {return(self$x*y)}")
# Błąd weval(expr, envir, enclos) : nie udało się znaleźć funkcji '='
clsR6$predict(3)
Добавление predict в определение класса ничего не меняет, та же ошибка. Есть ли какое-нибудь решение? Заранее спасибо.
> sessionInfo()
R version 3.1.1 (2014-07-10)
Platform: x86_64-pc-linux-gnu (64-bit)
locale:
[1] LC_CTYPE=pl_PL.UTF-8 LC_NUMERIC=C LC_TIME=pl_PL.UTF-8 LC_COLLATE=pl_PL.UTF-8 LC_MONETARY=pl_PL.UTF-8
[6] LC_MESSAGES=pl_PL.UTF-8 LC_PAPER=pl_PL.UTF-8 LC_NAME=C LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=pl_PL.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] R6_2.0
loaded via a namespace (and not attached):
[1] codetools_0.2-8 rpart_4.1-5 tools_3.1.1
>
Добавлено: после ответа great @G. Grothendieck у меня есть строковое определение функции, но, возможно, есть и другие элегантное решение.
library(R6)
clsTrnR6 <- R6Class("clsTrnR6",
lock=FALSE,
public = list(
x = NA,
initialize = function(x) {
self$x <- x
},
add_function = function(name, meth) {
self[[name]] <- meth
environment(self[[name]]) <- environment(self$add_function)
},
add_function2 = function(name, meth) {
eval(parse(text=paste0("predict <- ",meth)))
self[[name]] <- predict
environment(self[[name]]) <- environment(self$add_function)
}
)
)
clsR6 <- clsTrnR6$new(x=4)
clsR6$x
#[1] 4
clsR6$add_function2("predict", "function(y) y*self$x")
clsR6$predict(11)
#[1] 44
3 ответа:
Попробуйте это. Как и в Примере ссылочного класса, он добавляет функцию к объекту (а не к классу). Здесь
name
- символьная строка, содержащая имя функции / метода, аmeth
- сама функция / метод:clsTrnR6 <- R6Class("clsTrnR6", lock=FALSE, public = list( x = NA, initialize = function(x) { self$x <- x }, add_function = function(name, meth) { self[[name]] <- meth environment(self[[name]]) <- environment(self$add_function) } ) ) clsR6 <- clsTrnR6$new(x=4) clsR6$x #[1] 4 clsR6$add_function("predict", function(y) y*self$x) clsR6$predict(11) ## 44
Добавлено обратите внимание, что это также легко сделать с помощью proto. Он не требует специального
add_function
. Мы будем использовать верхний регистрP
для обозначения прото-объекта, который играет роль класса (называемого "чертой" в прото-виньетке) , и использовать нижний регистрp
для обозначения протообъекта, играющего роль экземпляра:Хотя обычно для обозначения объекта в proto используетсяlibrary(proto) P <- proto(new = function(., x) proto(x = x)) p <- P$new(x = 4) p$predict <- function(., y) .$x * y p$predict(11) ## 44
.
, Вы можете использовать имяself
(или любое другое имя, которое вам нравится) вместо.
, Если хотите.
Вы можете использовать
$set() method
на объекте генератора. Таким образом, вы измените определение класса, а не объекта.clsTrnR6$set("public", "predict", function(y) self$x*y) clsR6 <- clsTrnR6$new(x=4) clsR6$predict(3) [1] 12
Правка:
Изменение определения класса означает, что объект, созданный до использования модификатора $set, не будет иметь функцииpredict
.
Существует простое решение этой проблемы. Установите для метода класса значение по умолчанию
NULL
и обновите это значение в методеinitialize()
. Теперь вы можете изменить метод, как вам нравится, не получая этой ошибки. Например:aClass <- R6::R6Class("className", public = list( f = NULL, initialize = function(...) { self$f = sum }, update_f = function(x) { self$f = x } ) ) test <- aClass$new() test$f test$update_f(mean) test$f
Или можно изменить функцию на месте:
test$f <- median test$f
Это должно решить проблему. Я также разместил этот ответ здесь.