I have defined an S4 class called cell, instances of which I would like to assign to a 3×3 matrix (3×3 is chosen for definiteness). The following code works in R version 2.15.1 and reproduces R’s behavior in a simple case. I find that I can assign objects of type cell to a matrix whose entries are first initialized to empty lists with matrix(list(),3,3), after which I assign new objects of type cell to the entries. The question is: why does it work?
setClass("cell", representation = representation(
A="numeric", # a field
B="numeric")) # another one
# initialize the cell
setMethod("initialize", "cell", function(.Object, a,b) {
.Object@A <- a;
.Object@B <- b;
.Object})
createGrid <- function(a,b) {
grid <- matrix(list(),3,3) # note initialization to list()
for (i in 1:3 )
for (j in 1:3)
grid[[i,j]] <- new("cell",j,i);
grid}
This is a sample session:
> source("stackoverflow.R")
> grid <- createGrid(1,2)
> grid[[1,3]]
An object of class "cell"
Slot "A":
[1] 3
Slot "B":
[1] 1
> grid[[2,3]]
An object of class "cell"
Slot "A":
[1] 3
Slot "B":
[1] 2
Modifying createGrid() by changing the empty list assignment to grid<- matrix(0,3,3) will generate an error:
> grid <- createGrid0(1,2)
Error in grid[[i, j]] <- new("cell", j, i) :
more elements supplied than there are to replace
This is not surprising, but it did lead me to the working code.
The following attempt to define a 3×3 matrix of cells usingnew() fails:
> grid <- matrix(new("cell",1,2),3,3)
Error in as.vector(data) :
no method for coercing this S4 class to a vector
The question is, why does the first work?
Not really answering the question, but…
Usually it pays to think in terms of vectors, so instead of ‘cell’ maybe ‘Cell’ that represents the entire matrix. Here’s an implementation. The idea is that Cell extends matrix, with vectors that contain additional values.
We constrain A and B to have the same length with a validity function
and make a constructor that initialized the matrix of Cell with indicies into A and B.
The
...arguments are the non-data arguments (nrow, ncol, dimnames, etc) of matrix().We get some functionality for free (e.g., dim, nrow, ncol, length, …) but need to implement sub-setting and, presumably, other operations. For single-bracket sub-setting, we pass the operation down to the underlying matrix, and then use the resulting indicies to sub-set A and B, creating a new Cell instance with the updated values
Finally, implement a show method
And then in action:
There seem to be several benefits to this implementation. A matrix is supposed to have a single type, and that is the case here (with
cell, the elements of the matrix are lists and so could contain any data). There’s quite a bit of functionality inherited from matrix. Presumably you want to implement operations on your Cell matrix, and the underlying vectors A, B can be manipulated efficiently, e.g. via the group generic Arith,and then