3

I have the following code snippet:

require(lattice)
f.barchart <- function(...) {
    barchart(...,
        panel = function(x, y, ...) {
            panel.barchart(x, y, ...)
        }
    )
}

x <- data.frame(a = c(1,1,2,2), b = c(1,2,3,4), c = c(1,2,2,1))
f.barchart(a ~ b, data = x, groups = c)

Which results in the following error being thrown:

..3 used in an incorrect context, no ... to look in

When I use the following definition:

f.barchart <- function(...) {
    substitute(barchart(...,
        panel = function(x, y, ...) {
            panel.barchart(x, y, ...)
        }
    ))
}

I get:

barchart(a ~ b, data = x, groups = c,
    panel = function(x, y, ...) {
        panel.barchart(x, y, a ~ b, data = x, groups = c)
    })

I'm not sure if this is the cause of the above error but this would mean that the ellipsis is in panel.barchart gets wrongly expanded with the contents of the arguments given to f.barchart and not the panel function.

Is there a way to avoid this problem? How can I make the function work?

Quinten
  • 35,235
  • 5
  • 20
  • 53
lith
  • 929
  • 8
  • 23
  • I would be careful calling a variable `c`, because when you use `groups=c` it thinks you mean the function. However that is not the cause of your problem. The only point I found is that without the `group=c` argument, everything works. – Aniko Feb 18 '10 at 15:19

1 Answers1

8

As far as I understand, this happens not because of the nested ... but because of the first ... in barchart. So even this does not work:

f.barchart <- function(...) {
    barchart(...)
}

x <- data.frame(a = c(1,1,2,2), b = c(1,2,3,4), d = c(1,2,2,1))
print(f.barchart(a ~ b, data = x, groups = d))

I think this is because ... is a pairlist while barchart is expecting individual arguments. We need to unpack the pairlist, while making sure that we do not evaluate it too soon. The following is my solution:

f.barchart <- function(...) {
  cl<-match.call()
  cl$panel=function(x, y, ...) {
            panel.barchart(x, y, ...)
          }
  cl[[1]]=barchart
  eval(cl)
}

We capture the call to f.barchart using match.call which expands the dots, add the panel argument to the call, set the function to be called to barchart and then evaluate the call. Just as we added the panel argument, we could delete arguments which are used by f.barchart but which do not need to be passed to barchart.

Jyotirmoy Bhattacharya
  • 9,317
  • 3
  • 29
  • 38
  • 2
    Thanks a lot. There is only one minor modification I had to make: it seems necessary to use eval.parent instead of eval. My function now looks like this: f.barchart <- function(...) { cl <- f.merge.list(as.list(match.call()), list(MYOPTIONS)) cl[[1]] = barchart eval.parent(as.call(cl)) } Where f.merge.list is: f.merge.list <- function(a, b) { c(a, b[!names(b) %in% names(a)]) } – lith Feb 19 '10 at 11:22