简体   繁体   中英

How to prototype (initiate) S4 slots derived from other slots?

Suppose I have an S4 class

setClass("Person", representation(name = "character", first_letter = "character")

and I want to autofill the slot first_letter when an object is initiated. Something like

> jack <- new("Person", name = "Jack")
> print(jack)
# Slot "name":
# "Jack"
#
# Slot "first_letter":
# "J"

I think I should do something like this:

setClass("Person", 
  representation(name = "character", first_letter = "character"),
  prototype(first_letter = substring(self@name, 1, 1))
)

Of course, I could write a method to do this in a separate statement. I'm specifically interested in the case where the first_letter slot is initiated with information from another slot upon calling new .

Any help is greatly appreciated!

One way is to see the first_letter as a private field.

  1. Create a custom constructor ( initiate ) that set the private fields using other public fields
  2. Define set/get for public slots to keep the same logic when you change of of the other field.

Here one implementation:

## Note the use of `.` for private field
## this is just a convention
Person <- setClass("Person", representation(Name = "character", 
                                            first_letter. = "character"))

## define setter and getter for public slots
setGeneric("Name", function(x) standardGeneric("Name"))
setMethod("Name","Person",function(x)  x@Name)
setGeneric("Name<-", function(x,value) standardGeneric("Name<-"))
setMethod("Name<-","Person",function(x,value){
  x@Name <- value
  x@first_letter. <- substring(value, 1, 1)
  x
})
## define the constructor
## Note that we don't give an argument to the ctor 
## to init the private field
setMethod ("initialize", signature  = "Person",
           definition = function (.Object,
                                  name) {
             .Object@Name <- name
             .Object@first_letter. <- substring(name, 1, 1)
             return (.Object)
           })

Now , testing this by creating an object:

> p <- Person(name="Amine")
> p
An object of class "Person"
Slot "Name":
[1] "Amine"
## the slot is automatically initialized
Slot "first_letter.":
[1] "A"

Then is we change the slot value:

> Name(p) <- "Xvalue"
> p
An object of class "Person"
Slot "Name":
[1] "Xvalue"
## Again the private slot is updated
Slot "first_letter.":
[1] "X"

The prototype parameter would let you to give default values to your object slots. But as you are intending to do some kind of preprocessing of the data before assigning them to the slots in your object, you will need to define an initialization method for your class.

Also you may want to use the slot parameter... representation works the same, but it is intended for S3 inheritance which I am not sure is what you intend (check ?setClass )

Then define your class

setClass ("Person", slots = c (name = "character", first_letter = "character"))

or

setClass("Person", representation (name = "character", first_letter = "character"))

and the initialization method:

setMethod ("initialize", signature  = "Person",
           definition = function (.Object,
                                  name,
                                  first_letter = substring (name, 1, 1)) {
               .Object@name <- name
               .Object@first_letter <- first_letter
               return (.Object)
           })

Note : the .Object needs to be called like this (same name defined in the generic method). Also usually you have to remember to return it...

Now you should be able to initialize as you want

new("Person", name = "Jack")

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM