(This is revised to be closer to a previous, deleted answer, using slotName
and slot
rather than relying on attributes
). We could write a function that tests whether an instance is an S4 object, and if so extracts all the slots as a list and recurses
f = function(x) {
if (isS4(x)) {
nms <- slotNames(x)
names(nms) <- nms
lapply(lapply(nms, slot, object=x), f)
} else x
}
and then
A = setClass("A", representation(x="numeric"))
B = setClass("B", representation(a="A", b="numeric"))
f(B())
to arrive at a plain old list that we could use for whatever purposes we want.
$a
$a$x
numeric(0)
$a$class
[1] "A"
attr(,"package")
[1] ".GlobalEnv"
$b
numeric(0)
$class
[1] "B"
attr(,"package")
[1] ".GlobalEnv"
f
might need to be enhanced, e.g., to handle NULL values or S4 classes made from S3 classes via setOldClass
. The code to validObject
would be my choice of places to look for a more comprehensive traversal.
A generalization might make a visitor, along the lines of
visitLeavesWith <-
function(object, FUN, ...)
{
f = function(x) {
if (isS4(x)) {
slots <- setNames(slotNames(x), slotNames(x))
lapply(lapply(slots, slot, object=x), f)
} else FUN(x, ...)
}
f(object)
}
e.g.,
visitLeavesWith(B(), length)