Scope of dot-dot-dot Arguments

2020-03-21 05:24发布

I have a question on the scope of dot-dot-dot arguments. Consider the following function`foo =

foo <- function(x, ...){
   require(classInt);
   intvl = classIntervals(x, ...);
   return(intvl);
 }

The function works great for the following calls

x = runif(100, 0, 100);
y1 = foo(x, n = 5, style = 'quantile');
y2 = foo(x, style = 'equal');

But when I try to use the style = 'fixed' argument, which needs a fixedBreaks argument as well, I get

y3 = foo(x, style = 'fixed', fixedBreaks = seq(0, 100, 20))

Error in eval(expr, envir, enclos) : The ... list does not contain 2 elements

Note that the following works perfectly

y5 = classIntervals(x, style = 'fixed', fixedBreaks = seq(0, 100, 20))

I suspsect that this has something to do with scoping rules, but have been unable to put my finger on it. Any help on this would be very much appreciated.

EDIT. I cobbled up a simpler hack that makes it work. I think it is a match.call issue, as the same problem exists for style = 'pretty'. A quick look at the code shows that these are the two styles for which such match.calls are made, so quite likely this is the source of error. Any case, here is my proposed hack

foo2 <- function(x, ...){
  require(classInt);
  y = list(...); y$var = x;
  intvl = do.call('classIntervals', y);
}

y6 = foo2(x, style = 'fixed', fixedBreaks = seq(0, 100, 20))

I think Richie's answer to my question sheds some light on why my earlier code failed to work. But, I still don't understand why this one does.

标签: r scoping
1条回答
爱情/是我丢掉的垃圾
2楼-- · 2020-03-21 05:52

Inside the foo function, the ellipsis does contain 2 elements. Call this modification to see this.

foo <- function(x, ...){
   require(classInt);
   print(list(...))
   intvl = classIntervals(x, ...);
   return(intvl);
 }

Once classIntervals is called, the ellipsis changes, since arguments are matched differently. Here's the signature for that function

 classIntervals(var, n, style = "quantile", rtimes = 3, ...,
    intervalClosure = "left", dataPrecision = NULL)

In your call that fails, you have three arguments

foo(x, style = 'fixed', fixedBreaks = seq(0, 100, 20))

x gets matched up to var through positional matching (i.e., because it's in the first position in the signature in each case).

style gets matched up to style, via name matching (because they have the same name, duh).

fixedBreaks can't be matched by position or name so it ends up in the dots.

Thus the ellipsis contains 1 argument, and the error "The ... list does not contain 2 elements" is correct (if rather silly).


EDIT: Suggested fix to classIntervals. If you are contacting the author, then suggest replacing lines 42-43

mc <- match.call(expand.dots = FALSE)
fixedBreaks <- sort(eval(mc$...$fixedBreaks))

with

fixedBreaks <- list(...)$fixedBreaks

This is (I think) what they meant, and seemes to solve the silly error message.

查看更多
登录 后发表回答