I'm trying to forget refclasses (R5) and move to R6 but there is a problem with dynamic code. I would add a new function and it works in 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
Similar code doesn't work for 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)
Adding predict in class definition changes nothing, the same error. Is there any solution? Thanks in advance.
> 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
>
Added: After great @G.Grothendieck answer, I have string based function definition, but maybe there is more elegant solution.
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
Try this. Like the reference class example it adds a function to the object (not the class). Here name
is a character string containing the name of the function/method and meth
is the function/method itself:
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
Added Note that this is also easy to do using proto. It does not require a special add_function
. We will use an upper case P
to denote the proto object that plays the role of a class (called a "Trait" in the proto vignette) and use lower case p
to denote the proto object that plays the role of an instance:
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
Although its common to use .
to refer to the object in proto you can use the name self
(or any name you like) in place of .
if you prefer.
You can use $set() method
on the generator object. So you will change the class definition not the object.
clsTrnR6$set("public", "predict", function(y) self$x*y)
clsR6 <- clsTrnR6$new(x=4)
clsR6$predict(3)
[1] 12
Changing the class definition means that the object created prior to using the $set modifier will not have the predict
function.
There is a simple workaround for this problem. Set the default value of the class method to NULL
and update this value within the initialize()
method. Now, you can change the method as you like without receiving this error. For example:
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
Or, one can modify the function in-place:
test$f <- median
test$f
That should resolve the issue. I also posted this answer here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With