Search code examples
rfunctional-programmingellipsis

Reduce() and ... (ellipsis)


Following Hadley's advice in Advanced R, I'm trying to create a recursive version of a merger function using Reduce(). However, his example uses a vector of numbers, e.g.

Reduce(`+`, 1:3)
[1] 6

Whereas I want to be able to give multiple argument using ... (ellipsis). What I have in mind is something like this:

merge_datasets_multi = function(..., main=1, time=F) {
  #wrap with Reduce
  Reduce(function(x, y) merge_datasets(x, y, main=main, time=time), ...)

  #debugging, verify that the anonymous function works
  #(function(x, y) merge_datasets(x, y, main=main, time=time))(iris[1:50, ], iris[51:100, ])
}

The merge_datasets() function is in my personal package.

I have tested that the anonymous function I make works when given two arguments.

However, this throws an error:

merge_datasets_multi(iris[1:50, ], iris[51:100, ], iris[101:150, ])
Error in if (right) { : argument is not interpretable as logical
In addition: Warning message:
In if (right) { :
  the condition has length > 1 and only the first element will be used
Called from: Reduce(function(x, y) merge_datasets(x, y, main = main, time = time), 
    ...)

Presumably this is because the third argument (a data.frame) gets passed to a conditional, which fails to evaluate to true or false and throws the error.

Thus, if we instead give it a list of data.frames, it should work:

merge_datasets_multi(list(iris[1:50, ], iris[51:100, ], iris[101:150, ]))

And it does. However, this requires the extra step for the user of adding the data.frames into a list before calling merge_datasets_multi().

I tried passing as.list(...) as the argument to Reduce(), but it doesn't work. The output is just the first data.frame converted to a list.

How does one make a sequential function with Reduce() that takes ... as input?


Solution

  • The answer is apparently simple. Instead of trying as.list(), one should just use list(). See this previous answer.

    library(magrittr)
    merge_datasets_multi = function(..., main=1, time=F) {
      #wrap with Reduce
      Reduce(function(x, y) merge_datasets(x, y, main=main, time=time), list(...))
    }
    merge_datasets_multi(iris[1:50, ], iris[51:100, ], iris[101:150, ]) %>% dim
    [1] 150   5