Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modifying dots (...) inside a function

Tags:

r

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'
like image 784
Pierre Lapointe Avatar asked Dec 08 '15 15:12

Pierre Lapointe


2 Answers

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(‹…›).

like image 116
Konrad Rudolph Avatar answered Nov 02 '22 10:11

Konrad Rudolph


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")
like image 41
Philippe Grosjean Avatar answered Nov 02 '22 08:11

Philippe Grosjean