I would like to use a custom reference class inside another reference class, but this code fails:
nameClass <- setRefClass("nameClass", fields = list(first = "character",
last = "character"),
methods = list(
initialize = function(char){
chunks <- strsplit(char,"\\.")
first <<- chunks[[1]][1]
last <<- chunks[[1]][2]
},
show = function(){
cat("Special Name Class \n:")
cat("First Name:")
methods::show(first)
cat("Last Name:")
methods::show(last)
}
))
# this works fine
nameClass$new("tyler.durden")
When I try to add a second class that has a field of class nameClass this class cannot be initiated.
personClass <- setRefClass("personClass", fields = list(fullname = "nameClass",
occupation = "character"),
methods = list(
initialize = function(Obj){
nm <- deparse(substitute(Obj))
fullname <<- nameClass$new(nm)
occupation <<- Obj
}))
this just returns:
Error in strsplit(char, "\\.") :
argument "char" is missing, with no default
I could imagine a solution where nameClass is an S4 class but I reading a little made me kind of afraid to mix S4 and reference classes. Am I missing somthing or should I simply use an S4 classes when I want to define this particular name field more exactly than just 'character'?
I also found this thread with a promising title but could not figure out how this could solve my problem.
This is a variation of a common issue in the S4 system, where for inheritance to work a call to new with zero arguments has to work. This is because of the way inheritance is implemented, where the base class is instantiated and then populated with values from the derived class. To instantiate the base class requires creating it without any arguments. That you have a problem is illustrated with
> nameClass()
Error in .Internal(strsplit(x, as.character(split), fixed, perl, useBytes)) :
'x' is missing
and the solution is to provide a default argument in your initialize method
initialize=function(char=charcter()) { <...> }
or to otherwise arranging (e.g., by testing missing(char) in the body of initialize) for a no-argument call to the constructor to work.
Probably programming best practice would dictate that the initialize method takes a ... argument and has callSuper() in its body, so that derived classes can take advantage of base class (e.g., field assignment) functionality. To avoid problems with inadvertent matching of unnamed arguments, I think the signature should probably end up built around a template that looks like
initialize(..., char=character()) { callSuper(...) }
This scheme relies on a suitable definition of an 'empty' nameClass. The following probably has too much opinion and change of perspective to be immediately useful, but... It's tempting to think of nameClass as a 'row' in a data frame, but it's better (because R works best on vectors) to think of it as describing columns. With this in mind a reasonable representation of an 'empty' nameClass is where the first and last fields are each of length 0. Then
nameClass <- setRefClass("nameClass",
fields = list(first = "character", last = "character"),
methods = list(
initialize = function(..., char=character()){
if (length(char)) {
names <- strsplit(char, ".", fixed=TRUE)
.first <- vapply(names, "[[", character(1), 1)
.last <- vapply(names, "[[", character(1), 2)
} else {
.first <- character()
.last <- character()
}
callSuper(..., first=.first, last=.last)
}, show = function(){
.helper <- function(x)
sprintf("%s%s", paste(sQuote(head(x)), collapse=", "),
if (length(x) > 6) ", ..." else "")
cat("Special Name Class (n = ", length(first), ")\n", sep="")
cat("First names:", .helper(first), "\n")
cat("Last names:", .helper(last), "\n")
}))
with test cases like
> nameClass()
Special Name Class (n = 0)
First names:
Last names:
> nameClass(char="Paul.Simon")
Special Name Class (n = 1)
First names: 'Paul'
Last names: 'Simon'
> nameClass(char=c("Paul.Simon", "Frank.Sinatra"))
Special Name Class (n = 2)
First names: 'Paul', 'Frank'
Last names: 'Simon', 'Sinatra'
> nameClass(char=paste(LETTERS, letters, sep="."))
Special Name Class (n = 26)
First names: 'A', 'B', 'C', 'D', 'E', 'F', ...
Last names: 'a', 'b', 'c', 'd', 'e', 'f', ...
The derived class might be defined as
personClass <- setRefClass("personClass",
fields = list(fullname = "nameClass", occupation = "character"),
methods = list(
initialize = function(..., fullname=nameClass(),
occupation=character()) {
callSuper(..., fullname=fullname, occupation=occupation)
}))
with test cases like
personClass()
personClass(fullname=nameClass())
personClass(fullname=nameClass(), occupation=character())
personClass(fullname=nameClass(char="some.one"), occupation="job")
It seems that this is because you do not have a default constructor for your "nameClass":
nameClass$new()
Error in strsplit(char, "\\.") :
argument "char" is missing, with no default
If you modify your nameClass like this:
nameClass <- setRefClass("nameClass", fields = list(first = "character",
last = "character"),
methods = list(
initialize = function(s = NULL) {
if (!is.null(s) && nzchar(s)) {
chunks <- strsplit(s,"\\.")
first <<- chunks[[1]][1]
last <<- chunks[[1]][2]
}
},
show = function(){
cat("Special Name Class \n:")
cat("First Name:")
methods::show(first)
cat("Last Name:")
methods::show(last)
}
))
Then:
nameClass$new()
Special Name Class
:First Name:character(0)
Last Name:character(0)
and your personClass is now functional (yet the initialize method is quite odd):
personClass$new("tyler.durden")
Reference class object of class "personClass"
Field "fullname":
Special Name Class
:First Name:[1] "\"tyler"
Last Name:[1] "durden\""
Field "occupation":
[1] "tyler.durden"
Related
I have a nested list in the global environment of a R script.
anno <- list()
anno[['100']] <- list(
name = "PLACE",
color = "#a6cee3",
isDocumentAnnotation = T,
sublist = list()
)
person_sublist <- list()
person_sublist[['200']] <- list(
name = "ACTOR",
color = "#7fc97f",
isDocumentAnnotation = T,
sublist = list()
)
person_sublist[['300']] <- list(
name = "DIRECTOR",
color = "#beaed4",
isDocumentAnnotation = T,
sublist = list()
)
anno[['400']] <- list(
name = "PERSON",
color = "#1f78b4",
isDocumentAnnotation = T,
sublist = person_sublist
)
While running my process I interactively select elements via the id (100,200, ...). In return a want to add, delete or move elements in the list.
For this reason I thought of using a recursive function to navigate through the list:
searchListId <- function(parent_id = NULL, annotation_system = NULL)
{
for(id in names(annotation_system))
{
cat(paste(id,"\n"))
if(id == parent_id)
{
return(annotation_system[[id]]$sublist)
}
else
{
if(length(annotation_system[[id]]$sublist) > 0)
{
el <- searchListId(parent_id, annotation_system[[id]]$sublist)
if(!is.null(el))
return(el)
}
}
}
return(NULL)
}
searchListId('100', anno)
This functions returns the list() found in the sublist element of the matching element in the 'anno'-list. My problem is the global environment of R. If I manipulate something (delete, add, move something within the returned sublist) i need to reset the global variable with <<-. But in the case of a recursive function I only hold the current sublist in the context where the parent_id matches. How could one reference a global nested list in R while navigating though it via an recursive function? Is that even possible in R?
The calls I want to carry out in order to delete, add, or move elements in the list 'anno' are:
deleteListId('100', anno) #Should return the list without the element 100
addListId('400', anno) #Should return the list with a new element nested in '400'
switchListId('400','200', anno) #Should return a list where the elements with the according keys are switched.
The tricky part though is that I don't know how deep the recursive structure is. Normally I would use element references to manipulate them directly but how could a solution for manipulation of nested lists in R look like if I want to use recursion?
If possible, have the recursive function take a list, alter that, and return the new version. The reason I suggest this is because it's idiomatic R. R leans toward being a functional language, and part of that means state-based actions are discouraged. In general, functions should only modify state if that's all they do. For example, scale(x) doesn't affect the value stored in the x variable. But x <- scale(x) does, because the <- function (yes, it's a function) is meant to modify state.
Also, don't worry about memory unless you know it will be a problem based on past experience. Behind the scenes, R is pretty good at preventing needless copying, so trust it to do the right thing. This lets you work with simpler mental models.
A skeleton of how to recursively modify a list, without affecting the original:
anno <- list()
anno[['A1']] <- list(
sublist = list(
A3 = list(sublist = NULL),
A4 = list(sublist = list(A6 = list(sublist = NULL))),
A5 = list(sublist = NULL)
)
)
change_list <- function(x) {
for (i in seq_along(x)) {
value <- x[[i]]
if (is.list(value)) {
x[[i]] <- change_list(value)
} else {
if (is.null(value)) {
x[[i]] <- "this ws null"
}
}
}
x
}
change_list(anno)
# $A1
# $A1$sublist
# $A1$sublist$A3
# $A1$sublist$A3$sublist
# [1] "something new"
#
#
# $A1$sublist$A4
# $A1$sublist$A4$sublist
# $A1$sublist$A4$sublist$A6
# $A1$sublist$A4$sublist$A6$sublist
# [1] "something new"
#
#
#
#
# $A1$sublist$A5
# $A1$sublist$A5$sublist
# [1] "something new"
If you absolutely need to modify an item in the global namespace, use environments instead of lists.
anno_env <- new.env()
anno_env[["A1"]] <- new.env()
anno_env[["A1"]][["sublist"]] <- new.env()
anno_env[["A1"]][["sublist"]][["A3"]] <- NULL
anno_env[["A1"]][["sublist"]][["A4"]] <- NULL
change_environment <- function(environ) {
for (varname in ls(envir = environ)) {
value <- environ[[varname]]
if (is.environment(value)) {
change_environment(value)
} else {
environ[[varname]] <- "something new"
}
}
}
change_environment(anno_env)
anno_env[["A1"]][["sublist"]][["A3"]]
# [1] "something new"
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
I have a reference class Child which inherits from parents SuperA and SuperB. When in the initialize method of Child, I would like to invoke the initialize methods of both SuperA and SuperB in turn.
Thus, for instance, I have:
SuperA <- setRefClass("SuperA",
fields = list(a = "ANY"),
methods = list(
initialize = function(a) {
print(a)
initFields(a = a)
}
)
)
SuperB <- setRefClass("SuperB",
fields = list(b = "ANY"),
methods = list(
initialize = function(b) {
print(b)
initFields(b = b)
}
)
)
Child <- setRefClass("Child",
contains = c("SuperA", "SuperB"),
methods = list(
initialize = function(a, b) {
# attempt to invoke parent constructors one by one:
SuperA$callSuper(a)
SuperB$callSuper(b)
}
)
)
Child(1, 2) # want values 1 and 2 to be printed during construction of superclasses
However all I get is:
Error in print(a) : argument "a" is missing, with no default
So does anyone have any ideas on how to invoke a method belonging to a particular parent?
I'm not a heavy reference class user, but in S4 (on which reference classes are based) the best approach is often to avoid defining initialize methods (which have a complicated contract, both 'initialize' and 'copy' of named and unnamed arguments), to treat the constructor returned by setClass() as for internal use only, and to provide a user-facing constructor. Thus
.SuperA <- setRefClass("SuperA", fields = list(a = "ANY"))
.SuperB <- setRefClass("SuperB", fields = list(b = "ANY"))
.Child <- setRefClass("Child", contains = c("SuperA", "SuperB"))
Child <- function(a, b)
## any coercion, then...
.Child(a=a, b=b)
The result is
> Child(a=1, b=2)
Reference class object of class "Child"
Field "b":
[1] 2
Field "a":
[1] 1
From ?ReferenceClasses, the method $export(Class) coerces the object to an instance of 'Class', so
.SuperA <- setRefClass("SuperA", fields = list(a = "ANY"),
methods=list(foo=function() .self$a))
.SuperB <- setRefClass("SuperB", fields = list(b = "ANY"),
methods=list(foo=function() .self$b))
.Child <- setRefClass("Child", contains = c("SuperA", "SuperB"),
methods=list(foo=function() {
c(export("SuperA")$foo(), export("SuperB")$foo())
}))
Child <- function(a, b)
.Child(a=a, b=b)
Leads to
> Child(a=1, b=2)$foo()
[1] 1 2
I am attempting to write some classes in R. Here is the start of a neural network layer class. It is generating warnings and errors that I don't know how to correct.
# Slot definitions
setClass(
Class="neuralNetworkLayer",
representation=representation(
input = "vector",
linearOutput = "vector",
squashedOutput = "vector",
hasBias = "logical",
bias = "vector",
weights = "vector",
gains = "matrix",
squashFcn = "closure",
squashFcnDerivative = "closure"
)
)
# Constructors
NeuralNetworkLayer <- function(nInput,nOutput,hasBias=TRUE,squashFcn,squashFcnDerivative) {
nc = list(
input = c(rep(NA,nInput)),
linearOutput = c(rep(NA,nOutput)),
squashedOutput = c(rep(NA,nOutput)),
hasBias = hasBias,
bias = c(rep(NA,nOutput)),
weights = c(rep(NA,nOutput)),
gain = matrix(data=weights, nrow = nInput, ncol = nOutput),
squashFcn = squashFcn, # source of warning / error
squashFcnDerivative = squashFcnDerivative,
get = function(x) nc[[x]],
set = function(x, value) nc[[x]] <<- value,
props = list()
)
#Add a few more functions
nc$addProp = function(name, value) {
p <- nc$props
p[[name]] <- value
assign('props', p, envir=nc)
}
nc <- list2env(nc)
class(nc) <- "NeuralNetwork"
return(nc)
}
tanhDerivative <- function(x) {
d = 1 - tan(x)^2
return(d)
}
test <- NeuralNetworkLayer(nInput=4,nOutput=5,hasBias=TRUE,
squashFcn=tanh,squashFcnDerivative=tanhDerivative)
The messages generated are
Warning message:
undefined slot classes in definition of "neuralNetworkLayer": squashFcn(class "closure"),
squashFcnDerivative(class "closure")
Error in as.vector(x, mode) :
cannot coerce type 'closure' to vector of type 'any'
Both messages indicate that the base class closure can not be used for a slot. How to pass a function?
Taking the advice from the two answers, the following code can be generated. This addresses the original question of passing a function to a slot, and then using that function. For completeness, the revised neural network layer class is present.
setClass(
Class="neuralNetworkLayer",
representation=representation(
nInput = "numeric",
nOutput = "numeric",
squashFcn = "function",
derivSquashFcn = "function",
gains = "matrix",
hasBias = "logical",
bias = "matrix",
linOutput = "matrix",
squashOutput = "matrix"
)
)
getClass("neuralNetworkLayer")
getSlots("neuralNetworkLayer")
sf <- function(x){
f = tanh(x)
return(f)
}
dsf <- function(x) {
d = 1 - tan(x)^2
return(d)
}
# Create an object of class
hh = new("neuralNetworkLayer",squashFcn=sf,nInput=5,nOutput=5,hasBias=TRUE,
derivSquashFcn = dsf)
hh#squashFcn(3)
hh#derivSquashFcn(3)
The error/warning :
undefined slot classes in definition of "neuralNetworkLayer": squashFcn(class "closure")
means that slot is not defined beacuse the type "closure" is not defined.
You try To define a slot(attribute) as a generic function one idea is to use ANY ( the default for slot I think) type:
neuralNetworkLayer <-
setClass(
Class="neuralNetworkLayer",
representation=representation(
squashFcn = "ANY"
)
)
Then ,for example you instantiate your class like this :
# Constructors
hh = neuralNetworkLayer(squashFcn=function(x)print(x)) ## dummy function here
hh#squashFcn(10)
[1] 10
That's said , I think you should consider to define your functions slots as a real method(see setMethod) . Methods are to have typed (safer) objects Otheriwse there is no good reason the reason to use S4 system and easier to use S3 method.
I don't get why you are defining a neuralNetworkLayer S4 class while your constructor doesn't make any use of it. You just are creating a normal R list (no slots in the returning object of NeuralNetworkLayer). In your constructor you should call new somewhere.
Anyhow, your error doesn't have nothing to do with closure as you might think. You just didn't define the weights object that it happens to be also an R function. When you say:
weights = c(rep(NA,nOutput)),
gains = matrix(data=weights, nrow = nInput, ncol = nOutput),
you are creating an element of a list named weights, but you are not creating an object named weights. When you define the gains element, R just finds for weights its R function and tries to coerce it to put it into a matrix, giving the error. You can just define weights in your first line of NeuralNetworkLayer:
weights = c(rep(NA,nOutput))
and then, when you define nc, replace the first line I wrote above with:
weights = weights,
and your function won't give any error.
For the warning part of the definition of the S4 class, just use function instead of closure. However, as you have defined your constructor, you may well not define it at all.
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"