0

I have an R function, fun, with many parameters. I need to run fun several times, varying the parameters y and z but keeping the others the same. Rather than wrap every call of fun in a loop, I made y and z both NA by default; the function checks if is.na(y) or is.na(z) and, if so, calls itself recursively with each desired value of y and z in turn, like this:

fun <- function(a=defaultA, b=defaultB, ..., y = NA, z=NA) {
  if (is.na(y)) {
    for (yValue in 1:maxY) {
      fun(a, b, ..., y = yvalue, z = z)
    }
    return()
  }
  if (is.na(z)) {
    for (zValue in 1:maxZ) {
      fun(a, b, ..., y, z=zValue)
    }
    return()
  }
  stopifnot(!is.na(y) && !is.na(z))
  # Part of fun that does actual work follows.
  ...
}

The recursive fun calls are actually inconveniently long, a few hundred characters each, because they need to repeat every parameter given to fun. Is there any shortcut by which I can call fun recursively with all parameters but one preserved, or is there another, better way to accomplish what I'm trying to do?

For the curious: my actual fun produces a graph with data sets and options specified in the parameters, and y and z are Booleans that determine whether to graph specific outliers. This lets me produce with- and without-outliers versions of the graphs without duplicating the function calls or wrapping them in for-loops as for (y in c(TRUE, FALSE)) {for (z in c(TRUE, FALSE)) {fun(...)}}.

Connor Harris
  • 421
  • 5
  • 14
  • 1
    How about putting all the parameters in a vector/data.frame/list and passing that as a single argument to `fun`? – Andrew Gustar Mar 29 '17 at 14:21
  • That's a good thought! But the arguments aren't of the same type (so no vectors), and I rely heavily on the default arguments; I suppose I could make a list of the arguments and modify it as needed for each call, but I'd have to be careful to un-modify it afterward (otherwise I'll have non-default arguments from one call sticking around for the next). – Connor Harris Mar 29 '17 at 14:49

1 Answers1

2

match.call and sys.call are your friends here. See Why is match call useful?

In your case, here's a rewritten version of the first loop:

fun <- function(a = defaultA, b = defaultB, ..., y = NA, z = NA) {
  if (is.na(y)) {
    mc <- match.call(expand.dots = FALSE)
    for (yvalue in 1:maxY) {
      mc[['y']] <- yvalue
      eval(mc)
    }
  }
  ...
Community
  • 1
  • 1