Question

I am trying to overload the subscript operator ("[") for a custom class I've created. I am trying to figure out how to deal with the following issues.

  • How can you figure out if the operator is on lhs or rhs ? i.e. a[x] = foo vs foo = a[x]
  • When subscripting the entire dimension like foo = a[,x] how can I identify the first parameter ?
  • When using a[seq(x,y)] it seems to be expanding the entire sequence. Is there an easy way to get the first, step and last values without expanding ?

EDIT: My first point has received multiple answers. In the process I've figured out the answer to the second one. You can use the "missing" function to figure out which parameters are present.

Here is a sample code:

setMethod("[", signature(x="myClass"),
          function(x, i, j, k, l) {
              if (missing(i)) { i = 0 }
              if (missing(j)) { j = 0 }
              if (missing(k)) { k = 0 }
              if (missing(l)) { l = 0 }
          })

I have accepted an answer to this question as point number 3 is of least priority to me.

Was it helpful?

Solution 2

For the first bullet point there are two functions to overload:

  1. [
  2. [<-

The first function returns the value and the second sets the value. See the documentation for Extract.data.frame{base}

OTHER TIPS

Discover the Generic so you know what you are aiming to implement :

getGeneric('[')
# standardGeneric for "[" defined from package "base"
# function (x, i, j, ..., drop = TRUE) 

And :

getGeneric('[<-')
# standardGeneric for "[<-" defined from package "base"
# function (x, i, j, ..., value) 

Then you implement it like this for example :

`[<-.foo` <-
function(x, i, j, value) 
{
       ....

}

See the source for [.data.frame as an example. You've got an x, an i, and a j, in that order.

> `[.data.frame`
function (x, i, j, ..... )
> `[<-.data.frame`
function (x, i, j, value) 

You can do something similar:

obj <- structure(runif(10),class="myclass")
`[.myclass` <- function(x,i=NULL,j=NULL) {
  message("First parameter is ", i, "\n")
  message("Second parameter is ", j, "\n")
    sum(x)
}
obj[c(1,2,3)]

`[<-.myclass` <- function(x,i=NULL,j=NULL,value) {
    res <- unclass(x)
    res[i] <- value
    res
}
obj[1] <- 1

Order is irrelevant. The i's and j's will be right:

2 -> obj[2]
> obj
 [1] 1.0000000 2.0000000 0.3466835 0.3337749 0.4763512 0.8921983 0.8643395 0.3899895 0.7773207 0.9606180

As for the third bullet point, you could use something like this:

seq_parse <- function(s) {
    the_s <- deparse(substitute(s))
    env <- new.env()
    assign("seq", function(...) return(list(...)), envir=env)
    seq_params <- try(eval(parse(text=the_s), envir=env), silent=TRUE)
    if(!inherits(seq_params, "try-error")) {
        cat("parsing successful \n")
        return(seq_params)
    } else {
        stop("Failed to parse.")
    }
}

This function either returns the arguments to seq,

> seq_parse(seq(from=1,to=10,by=0.5))
working with params to seq_params 
$from
[1] 1

$to
[1] 10

$by
[1] 0.5 

or the conventional evaluated argument,

> seq_parse(1:5)
parsing successful 
[1] 1 2 3 4 5

However, I am not sure if that is good practice. Also you have to interpret carefully the parameters to seq, they could be unnamed, and instead of to there could be length.out etc.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top