Skip to content

matching last argument in function

10 messages · Alistair Gee, Erik Iverson, Gábor Csárdi +3 more

#
I often want to temporarily modify the options() options, e.g.

a <- seq(10000001, 10000001 + 10) # some wide object

with.options <- function(..., expr) {
  options0 <- options(...)
  tryCatch(expr, finally=options(options0))
}

Then I can use:

with.options(width=160, expr = print(a))

But I'd like to avoid explicitly naming the expr argument, as in:

with.options(width=160, print(a))

How can I do this with R's argument matching? (I prefer the expr as
the last argument since it could be a long code block. Also, I'd like
with.options to take multiple options.)

TIA
#
Alistair -

I don't believe this is possible.  The only way formal arguments (like 
expr) can be matched after a '...' is with *exact* name matching.  Why 
do you want to avoid explicitly naming the expr argument?

If you always want the expr argument last, you might be able to just use 
... as the sole argument to your function, and strip off the last 
element inside the function as 'expr', and use all but the last element 
as your list of options.  This requires that expr always be given last 
though.  Probably best just to explicitly name the expr argument.

Erik Iverson
Alistair Gee wrote:
#
It should be possible i think. You just supply all the arguments via
'...' and then cut off the last one. I don't see why this wouldn't work,
but maybe i'm missing something.

Gabor
On Tue, Feb 12, 2008 at 12:58:25PM -0600, Erik Iverson wrote:

  
    
#
Yes that will work, that's exactly what I was getting at in my second 
paragraph.  I wrote a function that uses this idea, except the (single) 
unnamed argument can occur anywhere in the function (not necessarily 
last). It will stop if there is more than one unnamed argument.

test2 <- function(...) {
   dots <- list(...)

   if(sum(dots.missing.name <- names(dots) %in% "") > 1)
     stop("Only one argument should have missing name.")

   expr.ind <- ifelse(any(names(dots) == "expr"),
                      which(names(dots) == "expr"),
                      which(dots.missing.name))

   expr <- dots[[expr.ind]]
   opts <- dots[-expr.ind]
   opts
}
Gabor Csardi wrote:
#
I couldn't get that to work, b/c I need the expr block to be evaluated
after the call to options(). I suspect that list(...) evaluates its
arguments. Here's what I did to your example:

test2 <- function(...) {
   dots <- list(...)    # <======= I think expr is evaluated here.
   if(sum(dots.missing.name <- names(dots) %in% "") > 1)
     stop("Only one argument should have missing name.")
   expr.ind <- ifelse(any(names(dots) == "expr"),
                      which(names(dots) == "expr"),
                      which(dots.missing.name))
   expr <- dots[[expr.ind]]
   opts <- dots[-expr.ind]
   o <- do.call(options, as.list(opts))
   tryCatch(expr, finally=options(o)) # <==== But I want expr evaluated here.
}

a <- seq(1000000, 1000000+15)

test2(width=200, print(a))


I'd like to have expr be unnamed, only b/c I tend to use functions
such as with.options() as flow-control constructs, and not requiring
the last argument to be named is just stylistically nicer, especially
when I nest several flow-control functions like with.options().

Of course, I can always use

  with.options(width=160, scipen=2, expr={
    some_code
  })

or

  with.options(list(width=160, scipen=2), {
    some_code
  })

But I prefer

  with.options(width=160, scipen=2, {
    some_code
  })

So, I'm hoping that this is indeed possible.
On Feb 12, 2008 11:34 AM, Erik Iverson <iverson at biostat.wisc.edu> wrote:
#
On Tue, 12 Feb 2008, Alistair Gee wrote:

            
You can't.  You could provide a list, though:

with.options <- function(optionlist,expr){
   option0<-options(optionlist)
   on.exit(options(options0))
   eval.parent(expr)
}

then 
with.options(width=160, print(a))
with.options(list(width=160, warn=1), print(a))


        -thomas

Thomas Lumley			Assoc. Professor, Biostatistics
tlumley at u.washington.edu	University of Washington, Seattle
#
Try this:

with.options <- function(...) {
    L <- as.list(match.call())[-1]
    len <- length(L)
    old.options <- do.call(options, L[-len])
    on.exit(options(old.options))
    invisible(eval.parent(L[[len]]))
}
[1]  1  2  3  4  5  6  7  8  9 10 11 12
[13] 13 14 15 16 17 18 19 20 21 22 23 24
[25] 25
[1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
On Feb 12, 2008 12:45 PM, Alistair Gee <alistair.gee at gmail.com> wrote:
#
On Feb 12, 2008, at 3:31 PM, Thomas Lumley wrote:

            
I'd second this idea: Always have two arguments, the second one being  
the expression and the first one being the list of options. This  
would make it look a lot more like "with" also.

Haris Skiadas
Department of Mathematics and Computer Science
Hanover College
#
Hi Gabor,

That almost works ... but it fails when I nest with.options() within
another function:

with.options <- function(...) {
   L <- as.list(match.call())[-1]
   len <- length(L)
   old.options <- do.call(options, L[-len])
   on.exit(options(old.options))
   invisible(eval.parent(L[[len]]))
}

with.width <- function(w)
  with.options(width=w, print(1:25))

m.with.width(10)
Enter a frame number, or 0 to exit

1: with.width(10)
2: with.options(width = w, print(1:25))
3: do.call(options, L[-len])
4: function (...)

I tried, unsuccessfully, to fix the problem by using eval.parent()
around do.call() and around L[-len].

This problem does not occur if I use my original implementation:

with.options <- function(..., expr) {
  options0 <- options(...)
  tryCatch(expr, finally=options(options0))
}

I realize that I can use my original implementation in this particular
case, but I'd like to have a single implementation that works
correctly, while not requiring explicitly naming the expr argument.

TIA
On Feb 12, 2008 12:43 PM, Gabor Grothendieck <ggrothendieck at gmail.com> wrote:
#
Add envir = parent.frame() to the do.call:


with.options <- function(...) {
    L <- as.list(match.call())[-1]
    len <- length(L)
    old.options <- do.call(options, L[-len], envir = parent.frame())
    on.exit(options(old.options))
    invisible(eval.parent(L[[len]]))
}

# test
with.options(width = 40, print(1:25))
test <- function(w) with.options(width = w, print(1:25))
test(40)
On Feb 13, 2008 1:29 PM, Alistair Gee <alistair.gee at gmail.com> wrote: