Creating a vector class
(better late than never, hopefully--a few comments on this example)
Duncan Murdoch wrote:
I'm trying to create a package for working on orientation data, i.e.
data where the observations are 3D rotations.
There are several different representations of orientations in common
use: SO(3) matrices, Euler angles, unit quaternions, etc. One thing
I'd like is to make it convenient to work in any representation, and
have conversions to others done as needed.
I'm trying to do all of this in S4 classes. Here's the current class
structure I'm using; please let me know if there's something wrong
with this:
The base class is abstract. All representations will deal with
vectors of orientations, but at this level I don't know how that will
be implemented:
setClass('orientation')
setIs('orientation', 'vector')
First questions: is this the way to say that orientations behave as
vectors?
It seems to say the right thing. 1) 'orientation' is a virtual class; 2) it extends 'vector', with no implication about the contents of either class and, since 'vector' is also virtual, no need to supply functions to coerce one to the other.
Do I need to say that? Should I say that?
Yes to the second question. To be honest, it probably doesn't make much practical difference now, but it's the "right thing" to do, as I understand your example. The assertion is that for all 'orientation' objects, the vector computations ( principally "[", "[[", and length) are meaningful & so 'orientation' objects should inherit methods written for class "vector" in terms of such computations. In practice, there aren't many such methods, but there may be more sometime. Furthermore, there are many computations that are currently not methods but that should conceptually be methods for "vector". The function rev, e.g.: R> rev function (x) if (length(x) > 0) x[length(x):1] else x <environment: namespace:base> This should really not be applied to non-vectors, and the definition is in terms of vector computations. A related point is that "[", etc. currently apply to objects for which they are not conceptually meaningful. Objects representing calls to functions, for example. (Both R and S-Plus allow it. R gets credit for not allowing subsets of function objects, though, which S-Plus does allow.) R> ttt = quote(plot(x^2, y, pch ="+")) R> ttt[2:3] x^2(y) R> rev(ttt) "+"(y, x^2, plot) !! The behavior of [ is perhaps unlikely to be changed in base. But if one could optionally attach a more strict method-based package, I think it would make sense to restrict "[" to classes that extend "vector" (which includes, of course, the basic vector datatypes).
One representation is as an SO(3) matrix (i.e. a 3x3 matrix with
determinant 1). I have a descendant class that stores these in a
3 x 3 x n array:
setClass('rotmatrix', representation(x = 'array'))
setIs('rotmatrix','orientation')
rotmatrix <- function(a) {
d <- dim(a)
if (length(d) < 3) d <- c(d,1)
a <- array(a, d)
stopifnot(dim(a)[1] == 3, dim(a)[2] == 3)
new('rotmatrix', x = a)
}
Other representations have other storage methods, e.g.
setClass('quaternion', representation(x = 'matrix'))
setIs('quaternion', 'orientation')
Now I want to make sure these work as vectors. I don't need to define
a '[' method for the abstract base class, do I?
No, you don't need or want such a method. In fact, it would be nice to
require that non-virtual classes extending 'orientation' DO have methods
for "[" (and, presumably, length()? what about "[["?)
There is a simple technique considered for the Omegahat OOP package that
might be a useful addition in S4 methods as well. We would introduce a
function, requireMethod, to force methods to be defined:
requireMethod("[", "orientation")
The function is just like setMethod, except without a method definition,
and its effect is to generate an error for an object that dispatches
this combination of function and signature. (Under the hood, it can
just call setMethod with a method that produces an appropriate error
message.)
The idea seems to convey what we want for virtual classes such as
'orientation'. For example, by defining your method below for
"rotmatrix", you satisfy the requirement, and block dispatch of the
error method.
I originally set this
definition for the rotmatrix class:
setMethod('[', 'rotmatrix',
def = function(x, i) rotmatrix(x@x[,,i,drop=FALSE])
)
... the rest was discussed before
Duncan Murdoch
______________________________________________ R-devel@stat.math.ethz.ch mailing list https://www.stat.math.ethz.ch/mailman/listinfo/r-devel
John M. Chambers jmc@bell-labs.com Bell Labs, Lucent Technologies office: (908)582-2681 700 Mountain Avenue, Room 2C-282 fax: (908)582-3340 Murray Hill, NJ 07974 web: http://www.cs.bell-labs.com/~jmc