From maechler at stat.math.ethz.ch  Sat Jul  7 10:20:27 2012
From: maechler at stat.math.ethz.ch (Martin Maechler)
Date: Sat, 7 Jul 2012 10:20:27 +0200
Subject: [Rd] creating a '[' method for an S4 class
In-Reply-To: <CAO-arWO=1+KAo1utsOjOq8vaaTC1M7bG3yoDPGZh_x_vB5-9tw@mail.gmail.com>
References: <CAO-arWO=1+KAo1utsOjOq8vaaTC1M7bG3yoDPGZh_x_vB5-9tw@mail.gmail.com>
Message-ID: <20471.61771.120942.446592@stat.math.ethz.ch>

>>>>> Benilton Carvalho <beniltoncarvalho at gmail.com>
>>>>>     on Sat, 7 Jul 2012 03:49:34 +0100 writes:

    > Hi,

    > I'm working on an S4 class that is expected to behave like an array.

    > I have some difficulties when defining '[' and I wonder if someone
    > could point me to the right direction:

    > 1) Call the S4 object "obj"
    > 2) Assume dim(obj) = c(10, 4, 2)
    > 3) Suppose someone calls: obj[1:3,] , which is a mistake, given
    > dim(obj); how do I detect such situations?

yes, a good question with a tricky answer.
Actually not only relevant for S4,  and hence one of the basic
tricks is already visible in   [.data.frame :
--- you need to use nargs() in addition to others more well
known tools ---

*and* it is trickier than you think:

Not only want you differentiate between  
  obj[ i , ]
and
  obj[ i , , ]

but more importantly,
between
  obj[ i ]
and
  obj[ i , ]
and that's  where nargs() comes into play
(as 'j', the 2nd argument is missing in both cases, you cannot
 use that check alone).

In my CRAN package  Rmpfr  {Interface R <--> MPFR library for
      	   	    	   	      arbitrary precision arithmetic}
I use S4 classes and methods extensively, as two argument
dispatch is the most natural paradigm for arithmetic.
Here is the part (from Rmpfr/R/array.R )
which implements "[" for 'mpfrArray':
{but you probably want the full package in src to understand all
 the details}:

.mpfrA.subset <- function(x,i,j, ..., drop) {
    nA <- nargs()
    if(getOption("verbose"))
        message(sprintf("nargs() == %d  mpfrArray indexing ... ", nA))

    r <- getD(x)# data part of x, a list()
    if(nA == 2) ## A[i]
        return(new("mpfr", r[i]))
    ## else: nA != 2 : nA > 2 -
    dim(r) <- (dx <- dim(x))
    dimnames(r) <- dimnames(x)
    r <- r[i,j, ..., drop=drop]
    if(drop && is.null(dim(r)))
        new("mpfr", r)
    else {
        D <- if(is.null(dr <- dim(r))) # ==> drop is FALSE; can this happen?
            rep.int(1L, length(r)) else dr
        x at Dim <- D
        x at Dimnames <- if(is.null(dn <- dimnames(r)))
            vector("list", length(D)) else dn
	if(length(D) == 2 && class(x) != "mpfrMatrix")
	    ## low-level "coercion" from mpfrArray to *Matrix :
	    attr(x,"class") <- getClass("mpfrMatrix")@className
        attributes(r) <- NULL
        setDataPart(x, r, check=FALSE)
    }
}

## "["
setMethod("[", signature(x = "mpfrArray", i = "ANY", j = "ANY", drop = "ANY"),
          .mpfrA.subset)

## this signature needs a method here, or it triggers the one for "mpfr"
setMethod("[", signature(x = "mpfrArray", i = "ANY", j = "missing",
                         drop = "missing"),
          .mpfrA.subset)




    > Thank you very much for your attention and time,
    > benilton



[Rd] creating a '[' method for an S4 class
Martin Maechler