9

I'm trying to modify the dots (...) inside a custom function. Here's a simplified example of my plot2 function, which displays a plot onscreen with type="p" (the default) and saves an svg with type="l". The problem surfaces when one of the ... plot options is already in the function. In this example, "type" is matched by multiple actual arguments.

plot2 <-function(...){
plot(...) #visible on screen

svg("c:/temp/out.svg") #saved to file
plot(...,type="l")
dev.off()
}

#This works
plot2(1:10)
#This does not work because type is redefined
plot2(1:10, type="o")

I have tried to put the dots in a list inside the function and modify it, but plot does not accept a list as an input.

#Does not work
plot2 <-function(...){
plot(...)

dots <<-list(...)
print(dots)
if("type" %in% names(dots)) dots$type="l"
print(dots)

svg("c:/temp/out.svg")
plot(dots)
dev.off()
}
plot2(1:10, type="o")
Error in xy.coords(x, y, xlabel, ylabel, log) : 
  'x' is a list, but does not have components 'x' and 'y'
Pierre Lapointe
  • 16,017
  • 2
  • 43
  • 56

2 Answers2

12

In cases where you want to forward a modified version of ..., you need to do two things:

  1. Capture the dots
  2. Forward the captured dots via do.call.

This works as follows:

plot2 = function (...) {
    # capture:
    dots = list(...)

    # modify:
    dots$type = 'l'

    # forward call:
    do.call(plot, dots)
}

In general, do.call(f, list(‹…›)) is equivalent to f(‹…›).

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
1

For what you want, there is a simpler (and better) way, and you do not need to touch to .... By explicitly defining the arguments that need a special treatment, you take them out of the catch all .... This is also a sane approach that is more explicit about what the function does (in its formal arguments). Here is how to do it:

plot2 <- function(x, type = "p", ...) {
  plot(x, type = type, ...) #visible on screen

  svg("out.svg") #saved to file
  plot(x, ..., type = "l")
  dev.off()
}

plot2(1:10)
plot2(1:10, type = "o")
  • The example I provided is simplified and your solution wouldn't be optimal with my actual functions. In my real life example, the `plot` function is a custom plot function with close to 50 arguments. The `plot2` function is just a wrapper to display the plot and save it in several formats. – Pierre Lapointe Dec 09 '15 at 15:15
  • Sorry, but still, it does work. It is not all the arguments that can be passed to ..., but only the few arguments you are manipulating in special way in your function that must be explicitly taken out of ... – Philippe Grosjean Dec 10 '15 at 21:18