I would like to store initial values in a Singleton in R, so I am using the R6 Singleton class. The examples show how to manipulate internal variables, but I cannot find a way to pass in values. I tried
library( R6 )
library( R6P )
Parameters <- R6::R6Class("Parameters", inherit = R6P::Singleton, public = list(
elements = NA,
initialize = function(...) { tmp<-list(...);if (length(tmp)>0) {self$elements<-tmp} },
retrieve = function(){ self$elements }
))
My goal is that if I create one instance with initial values, then subsequent instances will find the same value. When I run the above, however, I get:
> p <- Parameters$new( "a" )
> p$retrieve()
[[1]]
[1] "a"
> q <- Parameters$new()
> q$retrieve()
[1] NA
How can I get the second instance of the Singleton, q, to return the initialized value "a"?
------ Edited using #eduardokapp suggestion -------
(First, I don't know enough about SO rules to know whether to post an edit, post my own answer or ask a new question so I chose to edit the initial question. Apologies if that is wrong.)
Looking at the link to Hadley Wickham's "Advanced R" chapter, this edit gives me the same object in the "elements" variable like a Singleton:
library( pryr )
library( R6 )
library( R6P )
TemporaryFile <- R6Class("TemporaryFile", list(
path = NULL,
initialize = function(...) {
self$path <- list(...)
},
finalize = function() {
message("Cleaning up ", self$path)
unlink(self$path)
}
))
Parameters <- R6::R6Class("Parameters", inherit = R6P::Singleton, public = list(
name = NULL,
elements = TemporaryFile$new(),
initialize = function(name) { self$name = name },
retrieve = function(){ address(elements) }
))
which when I run gives:
> p <- Parameters$new( "a" )
> q <- Parameters$new( "b" )
> p$name
[1] "a"
> q$name
[1] "b"
> p$retrieve() == q$retrieve()
[1] TRUE
>
The problem now is how to assign an input value in "TemporaryFile$new()". Anything I try to put in ("path","...") gives an error. How do you assign a value to the internal class so that you can store a value on the first instantiation that will be shared by all subsequent instantiations? Am I just making this more complicated than it needs to be?
Two tricks. The first was to set the preserved value after construction of the instance. The second was to use an 'ifelse' function when assigning the preserved 'value'. Otherwise, it is written over at construction.
library( R6 )
library( R6P )
Parameters <- R6::R6Class("Parameters", inherit = R6P::Singleton, public = list(
input = NA,
value = ifelse( exists("self$value"), value, NA ),
assign = function(input) {self$value = input}
))
Test
> library(pry)
> p <- Parameters$new()
> p$assign(input="Hi")
> p$value
[1] "Hi"
> q <- Parameters$new()
> q$value
[1] "Hi"
> p$value
[1] "Hi"
> identical(address(p),address(q))
[1] TRUE
Every new instance of Parameters will hold the same 'value'.
Related
If I can run code before and after a user runs some code, how can I detect which variables were set or changed using base R? I can do this using identical() for non-environment objects. But is there a base-R solution for environments, including R6 classes?
Here's a solution using identical() which fails for envs/R6:
# Copy of initial vars
this_frame = sys.frame()
start_vars = ls()
start_copy = lapply(start_vars, get, envir = this_frame )
names(start_copy) = start_vars
# (user code here)
# Assess what's new and what's changed
end_vars = ls()
new_vars = end_vars[end_vars %in% start_vars == FALSE]
old_vars = end_vars[end_vars %in% start_vars == TRUE]
changed_vars = old_vars[sapply(old_vars, function(x) identical(get(x, envir = this_frame), start_copy[[x]])) == FALSE]
I'm writing a package that lets users run code in a separate session. I'd like to return only objects that were changed.
This solution detects changes in an environment, sub-environments, and R6-classes.
General approach
run start_state = env_as_list() on sys.frame()which stores everything in a list and recursively converts all environments/R6 and sub-environments/R6 to list.
Let the user manipulate stuff
Run end_state = env_as_list() and use identical() to detect changes between start_state and end_state.
env_as_list = function(env) {
rapply(
object = as.list(env, all.names = TRUE),
f = function(x) {
if ("R6" %in% class(x)) {
# R6 to list without recursion
x = as.list(x, all.names = TRUE)
x$.__enclos_env__$self = NULL
x$.__enclos_env__$super = NULL
env_as_list(x)
} else if (is.environment(x)) {
env_as_list(x)
} else {
stop("Impossible to get here")
}
},
classes = c("environment", "R6"),
how = "replace"
)
}
Demonstration
Let's test it: let's fill globalenv() with a some stuff to begin with:
R6_class = R6::R6Class("Testing", list(a = 1))
my_R6 = R6_class$new()
my_env = new.env()
my_env$sub_env = new.env()
my_env$sub_env$some_value = 2
my_regular = rnorm(5)
Snapshot time!
start_state = env_as_list(sys.frame())
Let the user play:
my_R6$a = 99 # Change R6
new_regular = 3 # new var
my_env$sub_env$some_value = 99 # Change sub-environment
Snapshot again!
end_state = env_as_list(sys.frame())
end_state$start_state = NULL # don't include this
Did nothing change?
> identical(start_state, end_state))
# FALSE
Which variables changed?
> is_same = lapply(names(end_state), function(x) identical(start_state[[x]], end_state[[x]]))
> names(end_state)[is_same == FALSE]
# "my_env" "new_regular" "my_R6"
Bonus
You can also use this to compute the size of an environment, including all R6 and sub-environments. Simply:
object.size(env_as_list(globalenv()))
The identical() function seems to give the correct answer, but the documentation doesn't explicitly discuss object references. The closest note in the documentation is:
Checking equality for two large, complicated objects can take longer
if the objects are identical or nearly so, but represent completely
independent copies.
Some examples of using identical():
QuickClass <- R6::R6Class("QuickClass",
public = list(
initialize = function(x) {
private$px <- x
}
),
active = list(
x = function(px) {
if(missing(px)) return(private$px)
else private$px <- px
}
),
private = list(
px = 0
)
)
> a <- QuickClass$new(1)
> identical(a, a)
[1] TRUE
> b <- a
> identical(a, b)
[1] TRUE
> c <- QuickClass$new(2)
> identical(a, c)
[1] FALSE
> d <- QuickClass$new(1)
> identical(a, d)
[1] FALSE
So, identical looks to do what is needed, I just want to check if there is a better way e.g. a specific function that just compares object references so may be faster and more directly applicable. identical() looks like it can resort to field-by-field comparisons.
Contrast clause: This question is similar to In R, how can I check if two variable names reference the same underlying object? - however that question is quite old (pre-R6 classes) and the answers discuss using low-level techniques that I would rather avoid.
You could assign a random id for each new object and compare the ids.
library(R6)
MyClass <- R6Class(
"MyClass",
public = list(
id = NULL,
initialize = function(){
self$id <- as.character(sample(1:10^6, 1))
}
)
)
MyClass$equal <- function(object1, object2){
object1$id == object2$id
}
A <- MyClass$new()
B <- MyClass$new()
MyClass$equal(A, A)
# TRUE
MyClass$equal(A, B)
# FALSE
I'm new to R and I'm currently trying to supply the enumeration-like argument to the R function (or the RC/R6 class method), I currently use character vector plus match.arg similar to the following:
EnumTest = function(enum = c("BLUE", "RED", "BLACK")) {
enumArg <-
switch(
match.arg(enum), "BLUE" = 0L, "RED" = 1L, "BLACK" = 2L
)
switch(enumArg,
# do something
)
}
Is there are better/more concise way to imitate enum-like behavior in R? E.g. one big problem that user has to know the set of possible values for the argument and manually type it as a string - without any suggestion or auto-completion...
If there is no other better way, one thing that could improve above approach - it'd be nice to make it more concise by say predefining enums globally or say as private members of R6 class:
Color <- c("BLUE", "RED", "BLACK")
Then one could (re)use it in one or more function definitions, e.g.:
EnumTest = function(enum = Color) {
...
However, I'm not sure how to use this Color vector in match.arg function. It'd be nice if I could define Color as a map with keys being actual color values and values being integer representation - but I'm not sure how sensible that is.. Anyways, maybe there are more common neat approaches exist.
The main goal would be to provide an easy-to-use intuitive interface to the user of my package and functions (e.g. easy way to find the set of possible values, tab-completion, auto-suggestion, etc..), followed by standardized development of such functions using enum-like arguments
How about using a function that defines the enum by returning list(a= "a", ...)? You can then either assign the returned vector to a variable and use it in context, or use the function directly. Either a name or an integer reference will work as an index, although you have to use the unlist version of the index lookup, [[, otherwise you get a list with one element.
colorEnum <- function() {
list(BLUE = "BLUE", RED = "RED", BLACK = "BLACK")
}
colorEnum()$BLUE
#> [1] "BLUE"
colorEnum()[[1]]
#> [1] "BLUE"
colorEnum()[1]
#> $BLUE
#> [1] "BLUE"
col <- colorEnum()
col$BLUE
#> [1] "BLUE"
col[[1]]
#> [1] "BLUE"
col$BAD_COLOR
#> NULL
col[[5]]
#> Error in col[[5]] : subscript out of bounds
You can get the list of names for use in a match, i.e. your function parameter could be
EnumTest = function( enum = names(colorEnum()) { ...
You can actually abbreviate too, but it must be unique. (If you use RStudio, since col is a list, it will suggest completions!)
col$BLA
#> [1] "BLACK"
col$BL
#> NULL
If you want more sophisticated enum handling, you could assign S3 classes to the thing returned by your enum constructor function and write a small collection of functions to dispatch on class "enum" and allow case-insensitive indexing. You could also add special functions to work with a specific class, e.g. "colorEnum"; I have not done that here. Inheritance means the list access methods all still work.
colorEnum2 <- function() {
structure(
list(BLUE = "BLUE", RED = "RED", BLACK = "BLACK"),
class= c("colorEnum2", "enum", "list")
)
}
# Note, changed example to allow multiple returned values.
`[.enum` <- function(x, i) {
if ( is.character( i ))
i <- toupper(i)
class(x) <- "list"
names(as.list(x)[i])
}
`[[.enum` <- function(x, i, exact= FALSE) {
if ( is.character( i ))
i <- toupper(i)
class(x) <- "list"
as.list(x)[[i, exact=exact]]
}
`$.enum` <- function(x, name) {
x[[name]]
}
col <- colorEnum2()
# All these return [1] "RED"
col$red
col$r
col[["red"]]
col[["r"]]
col["red"]
col[c("red", "BLUE")]
#> [1] "RED" "BLUE"
col["r"]
[1] NA # R does not matches partial strings with "["
These override the built in [, [[ and $ functions when the thing being indexed is of class "enum", for any "enum" classed objects. If you need another one, you just need to define it.
directionEnum <- function() {
structure(
list(LEFT = "LEFT", RIGHT = "RIGHT"),
class= c("directionEnum", "enum", "list")
)
}
directionEnum()$l
#> [1] "LEFT"
If you need several enum objects, you could add a factory function enum that takes a vector of strings and a name and returns an enum object. Most of this is just validation.
enum <- function(enums, name= NULL) {
if (length(enums) < 1)
stop ("Enums may not be empty." )
enums <- toupper(as.character(enums))
uniqueEnums <- unique(enums)
if ( ! identical( enums, uniqueEnums ))
stop ("Enums must be unique (ignoring case)." )
validNames <- make.names(enums)
if ( ! identical( enums, validNames ))
stop( "Enums must be valid R identifiers." )
enumClass <- c(name, "enum", "list")
obj <- as.list(enums)
names(obj) <- enums
structure( obj, class= enumClass)
}
col <- enum(c("BLUE", "red", "Black"), name = "TheColors")
col$R
#> [1] "RED"
class(col)
#> [1] "TheColors" "enum" "list"
side <- enum(c("left", "right"))
side$L
#> [1] "LEFT"
class(side)
#> [1] "enum" "list"
But now this is starting to look like a package...
I like to use environments as replacement for enums because you can lock them to prevent any changes after creation. I define my creation function like this:
Enum <- function(...) {
## EDIT: use solution provided in comments to capture the arguments
values <- sapply(match.call(expand.dots = TRUE)[-1L], deparse)
stopifnot(identical(unique(values), values))
res <- setNames(seq_along(values), values)
res <- as.environment(as.list(res))
lockEnvironment(res, bindings = TRUE)
res
}
Create a new enum like this:
FRUITS <- Enum(APPLE, BANANA, MELON)
We can the access the values:
FRUITS$APPLE
But we cannot modify them or create new ones:
FRUITS$APPLE <- 99 # gives error
FRUITS$NEW <- 88 # gives error
I just faced this exact problem and could only find this SO question. The objectProperties package mention by Paul seems abandoned (it produces several warnings) and has lots of overhead for such a simple (in principle) problem. I came up with the following lightweight solution (depends only on the stringi package), which reproduces the feel of Enums in C languages. Maybe this helps someone.
EnumTest <- function(colorEnum = ColorEnum$BLUE) {
enumArg <- as.character(match.call()[2])
match.arg(enumArg, stringi::stri_c("ColorEnum$", names(ColorEnum)))
sprintf("%s: %i",enumArg,colorEnum)
}
ColorEnum <- list(BLUE = 0L, RED = 1L, BLACK = 2L)
Here is a simple method which supports enums with assigned values or which use the name as the value by default:
makeEnum <- function(inputList) {
myEnum <- as.list(inputList)
enumNames <- names(myEnum)
if (is.null(enumNames)) {
names(myEnum) <- myEnum
} else if ("" %in% enumNames) {
stop("The inputList has some but not all names assigned. They must be all assigned or none assigned")
}
return(myEnum)
}
If you are simply trying to make a defined list of names and don't care about the values you can use like this:
colors <- makeEnum(c("red", "green", "blue"))
If you wish, you can specify the values:
hexColors <- makeEnum(c(red="#FF0000", green="#00FF00", blue="#0000FF"))
In either case it is easy to access the enum names because of code completion:
> hexColors$green
[1] "#00FF00"
To check if a variable is a value in your enum you can check like this:
> param <- hexColors$green
> param %in% hexColors
Update 07/21/2017: I have created a package for enumerations in R:
https://github.com/aryoda/R_enumerations
If you want to use self-defined enum-alike data types as arguments of R functions that support
automatic translation of enum item names to the corresponding integer values
code auto completion (e. g. in RStudio)
clear documentation in the function signature which values are allowed as actual function parameters
easy validation of the actual function parameter against the allowed (integer) enum item values
you can define your own match.enum.arg function, e. g.:
match.enum.arg <- function(arg, choices) {
if (missing(choices)) {
formal.args <- formals(sys.function(sys.parent()))
choices <- eval(formal.args[[as.character(substitute(arg))]])
}
if(identical(arg, choices))
arg <- choices[[1]][1] # choose the first value of the first list item
allowed.values <- sapply(choices,function(item) {item[1]}) # extract the integer values of the enum items
if(!is.element(arg, allowed.values))
stop(paste("'arg' must be one of the values in the 'choices' list:", paste(allowed.values, collapse = ", ")))
return(arg)
}
Usage:
You can then define and use your own enums like this:
ColorEnum <- list(BLUE = 1L, RED = 2L, BLACK = 3L)
color2code = function(enum = ColorEnum) {
i <- match.enum.arg(enum)
return(i)
}
Example calls:
> color2code(ColorEnum$RED) # use a value from the enum (with auto completion support)
[1] 2
> color2code() # takes the first color of the ColorEnum
[1] 1
> color2code(3) # an integer enum value (dirty, just for demonstration)
[1] 3
> color2code(4) # an invalid number
Error in match.enum.arg(enum) :
'arg' must be one of the values in the 'choices' list: 1, 2, 3
Is it possible to call or set values for more then one slot?
A<-setClass(Class="A",slot=c(name="character",type="character"))
a<-A()
slot(object,c("name","type"),check=T)
Do I have to write own getSlot and setSlot methods? And how to that in R5?
AB <- setRefClass("AB", fields=c(name="character"),
methods=list(getName=AB.getName)
)
AB.getName<-function(object){
object$name
}
a<-AB(name="abc")
AB.getName(a)
This answer applies to reference classes.
Let's start with the simplest definition of AB, without any methods.
AB <- setRefClass(
"AB",
fields = list(
name = "character"
)
)
You can retrieve the value of the name field in the same way you would a list.
ab <- AB$new(name = "ABC")
ab$name
## [1] "ABC"
(ab$name <- "ABCD")
## [1] "ABCD"
It is possible to autogenerate accessor methods to get and set the name field.
AB$accessors("name")
ab$getName()
ab$setName("ABCDE")
This is really pointless though since it has the exactly same behaviour as before, but with more typing. What can be useful is to do input checking (or other custom behaviour) when you set a field. To do this, you can add a setName method that you write yourself.
AB$methods(
setName = function(x)
{
if(length(x) > 1)
{
warning("Only using the first string.")
x <- x[1]
}
name <<- x
}
)
ab$setName(letters)
## Warning message:
## In ab$setName(letters) : Only using the first string.
It is also possible (and usually more useful) to define this method when you assign the reference class template.
AB <- setRefClass(
"AB",
fields = list(
name = "character"
),
methods = list(
setName = function(x)
{
if(length(x) > 1)
{
warning("Only using the first string.")
x <- x[1]
}
name <<- x
}
)
)
Response to comment:
Yes that works, but:
getFieldNames is more maintainable if implemented as names(AB$fields()).
When defining fields in setRefClass, use a list. For example, list(name="character", var2="character").
When assigning an instance of a reference class, use new. For example, AB$new(name="abc",var2="abc")
In S4, the default initialize method allows one to write
A <- setClass(Class="A", slot=c(name="character",type="character"))
a <- A(name="abc", type="def")
initialize(a, name="cde", type="fgh")
Your own initialize methods (if any -- I think it's usually best to avoid them) have to be written to allow for this use. There is no default way to convert an S4 representation to a list.
You could incorporate these ideas into your own generics / methods with something like
setGeneric("values", function(x, ...) standardGeneric("values"))
setMethod("values", "A", function(x, ...) {
slts = slotNames(x)
lapply(setNames(slts, slts), slot, object=x)
})
setGeneric("values<-", function(x, ..., value) standardGeneric("values<-"))
setReplaceMethod("values", c(x="A", value="list"), function(x, ..., value) {
do.call("initialize", c(x, value))
})
with
> a <- A(name="abc", type="def")
> values(a) = list(name="cde", type="fgh")
> values(a)
$name
[1] "cde"
$type
[1] "fgh"
I am trying to create a simple reference class in R. Here is my code (R beginner):
MyClass <- setRefClass("MyClass",
fields = list(a = "numeric",
b = "numeric"),
methods = list(
initialize <- function(){
print("Initializing")
a <<- 1
b <<- 2
},
printValues <- function(){
print(a)
print(b)
}
)
)
a <- MyClass$new()
a$printValues()
This produces the following error for the last line, a$printValues:
Error in envRefInferField(x, what, getClass(class(x)), selfEnv) :
"printValues" is not a valid field or method name for reference class “MyClass”
Also, the initializer method is not being called ?
Can someone point me to where the issue lies here ? Many thanks in advance.
The methods argument to setRefClass needs to be a named list. The problem is you are using the assign operator <- instead of = when defining your list. See the difference between
list(a = 1, b = 2)
# $a
# [1] 1
#
# $b
# [1] 2
which returns a named list and
list(a <- 1, b <- 2)
# [[1]]
# [1] 1
#
# [[2]]
# [1] 2
which creates a and b in your environment and returns an unnamed list.
So when passing your methods, you need to use =:
methods = list(initialize = function [...],
printValues = function [...]