19

In the documentation of sapply and replicate there is a warning regarding using ...

Now, I can accept it as such, but would like to understand what is behind it. So I've created this little contrived example:

innerfunction<-function(x, extrapar1=0, extrapar2=extrapar1)
{
    cat("x:", x, ", xp1:", extrapar1, ", xp2:", extrapar2, "\n")
}

middlefunction<-function(x,...)
{
    innerfunction(x,...)
}

outerfunction<-function(x, ...)
{
    cat("Run middle function:\n")
    replicate(2, middlefunction(x,...))
    cat("Run inner function:\n")
    replicate(2, innerfunction(x,...))
}

outerfunction(1,2,3)
outerfunction(1,extrapar1=2,3)
outerfunction(1,extrapar1=2,extrapar2=3)

Perhaps I've done something obvious horribly wrong, but I find the result of this rather upsetting. So can anyone explain to me why, in all of the above calls to outerfunction, I get this output:

Run middle function:
x: 1 , xp1: 0 , xp2: 0 
x: 1 , xp1: 0 , xp2: 0 
Run inner function:
x: 1 , xp1: 0 , xp2: 0 
x: 1 , xp1: 0 , xp2: 0

Like I said: the docs seem to warn for this, but I do not see why this is so.

Robotski
  • 38
  • 1
  • 7
Nick Sabbe
  • 11,684
  • 1
  • 43
  • 57

4 Answers4

12

?replicate, in the Examples section, tells us explicitly that what you are trying to do does not and will not work. In the Note section of ?replicate we have:

     If ‘expr’ is a function call, be aware of assumptions about where
     it is evaluated, and in particular what ‘...’ might refer to.  You
     can pass additional named arguments to a function call as
     additional named arguments to ‘replicate’: see ‘Examples’.

And if we look at Examples, we see:

 ## use of replicate() with parameters:
 foo <- function(x=1, y=2) c(x,y)
 # does not work: bar <- function(n, ...) replicate(n, foo(...))
 bar <- function(n, x) replicate(n, foo(x=x))
 bar(5, x=3)

My reading of the docs is that they do far more than warn you about using ... in replicate() calls; they explicitly document that it does not work. Much of the discussion in that help file relates to the ... argument of the other functions, not necessarily to replicate().

Gavin Simpson
  • 170,508
  • 25
  • 396
  • 453
  • Note that in the example, event the "correct" way doesn't work properly - it is replicated `x` times (3) rather than `n` times (5). – James Jul 15 '11 at 09:45
  • 1
    @James - not on my system. I get a 2*5 matrix for `bar(5, x = 3)` – Gavin Simpson Jul 15 '11 at 09:47
  • Oops, my mistake. Had a typo when transcribing the examples. – James Jul 15 '11 at 09:53
  • 1
    @Gavin: The quote from `?replicate` is the warning I mentioned in my question. What I would like to know is what this warning means? 'where it is evaluated' and 'what ... might refer to' are somewhat cryptic to me. So, again: I accept that it doesn't work, but _why_ doesn't it work? This might greatly help my/others' understanding of argument passing/parsing in R. – Nick Sabbe Jul 15 '11 at 10:06
  • 1
    @Nick sorry for late reply; To my mind, the reason this doesn't work is that you are *not* calling the function in `expr`, you are just evaluating `expr`. Because you are not calling it, there is no passing of arguments from the outer call `...` to the `expr`. This is a subtle difference but is very important; it is as if you called `middlefunction(...)` exactly like that in the global environment (you can't because R doesn't allow you to use `...` interactively like that) where `...` doesn't refer to anything. – Gavin Simpson Jul 16 '11 at 08:41
6

If you look at the code for replicate:

> replicate
function (n, expr, simplify = TRUE) 
sapply(integer(n), eval.parent(substitute(function(...) expr)), 
    simplify = simplify)
<environment: namespace:base>

You see that the function is evaluated in the parent frame, where the ... from your calling function no longer exists.

James
  • 65,548
  • 14
  • 155
  • 193
1

There actually is a way to do this:

# Simple function:
ff <- function(a,b) print(a+b)

# This will NOT work:
testf <- function(...) {
  replicate(expr = ff(...), n = 5)
}
testf(45,56) # argument "b" is missing, with no default

# This will:
testf <- function(...) {
  args <- as.list(substitute(list(...)))[-1L]
  replicate(expr = do.call(ff, args), n = 5)
}
testf(45,56) # 101
Antoine Lizée
  • 3,743
  • 1
  • 26
  • 36
0

An alternative way to do that:

g <- function(x, y) x + y

f <- function(a = 1, ...) {
    arg_list <- list(...)
    replicate(n = 3, expr = do.call(g, args = arg_list))
}

f(x = 1, y = 2)
irudnyts
  • 21
  • 4