Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Behavior of do.call() in the presence of arguments without defaults

This question is a follow-up to a previous answer which raised a puzzle.

Reproducible example from the previous answer:

Models <- list( lm(runif(10)~rnorm(10)),lm(runif(10)~rnorm(10)),lm(runif(10)~rnorm(10)) )
lm1 <- lm(runif(10)~rnorm(10))
library(functional)
# This works
do.call( Curry(anova, object=lm1), Models )
# But so does this
do.call( anova, Models )

The question is why does do.call(anova, Models) work fine, as @Roland points out?

The signature for anova is anova(object, ...)

anova calls UseMethod, which should* call anova.lm which should call anova.lmlist, whose first line is objects <- list(object, ...), but object doesn't exist in that formulation.

The only thing I can surmise is that do.call might not just fill in ellipses but fills in all arguments without defaults and leaves any extra for the ellipsis to catch? If so, where is that documented, as it's definitely new to me!

* Which is itself a clue--how does UseMethod know to call anova.lm if the first argument is unspecified? There's no anova.list method or anova.default or similar...

like image 264
Ari B. Friedman Avatar asked Aug 07 '13 16:08

Ari B. Friedman


2 Answers

In a regular function call ... captures arguments by position, partial match and full match:

f <- function(...) g(...)
g <- function(x, y, zabc) c(x = x, y = y, zabc = zabc)

f(1, 2, 3)
# x    y zabc 
# 1    2    3     
f(z = 3, y = 2, 1)
# x    y zabc 
# 1    2    3     

do.call behaves in exactly the same way except instead of supplying the arguments directly to the function, they're stored in a list and do.call takes care of passing them into the function:

do.call(f, list(1, 2, 3))
# x    y zabc 
# 1    2    3     
do.call(f, list(z = 3, y = 2, 1))
# x    y zabc 
# 1    2    3     
like image 152
hadley Avatar answered Nov 11 '22 17:11

hadley


I think it is worth stressing that the names of the list elements do matter. Hadley mentioned it, but it can be an annoyance. Consider the next example:

x <- rnorm(1000)
y <- rnorm(1000)
z <- rnorm(1000) + 0.2

Models <- list()
Models$xy <- lm(z~x)
Models$yz <- lm(z~y)

# This will fail, because do.call will not assign anything to the argument "object" of anova
do.call(anova, Models)
# This won't
do.call(anova, unname(Models))
like image 36
Antoine Lizée Avatar answered Nov 11 '22 18:11

Antoine Lizée