I'm writing wrappers for some functions that change some of the default
arguments. I'd rather not list all of the arguments for the low level
functions because there are about a dozen wrapper functions, and about
20 arguments to lowlevel. Instead I'm trying something like this:
lowlevel <- function(longname = 1) {
cat("longname = ", longname, "\n")
}
wrapper <- function(...) {
newargs <- list(longname = 2)
newargs[names(list(...))] <- list(...)
do.call("lowlevel", newargs)
}
This almost works:
> wrapper()
longname = 2
> wrapper(longname = 3)
longname = 3
But it fails if I try to use partial argument matching:
> wrapper(long=4)
Error in lowlevel(longname = 2, long = 4) :
unused argument(s) (long ...)
because long isn't matched to longname. Is there a reasonable way to do
this (e.g. using pmatch or charmatch) other than listing all the low
level arguments in the argument list to wrapper?
Duncan Murdoch
Expanding partial names
12 messages · Gabor Grothendieck, Charles Dupont, Hadley Wickham +2 more
Try this:
wrapper <- function(...) {
args <- list(...)
if (length(args)) {
nf <- names(formals(lowlevel))
nams <- nf[pmatch(names(args), nf)]
args <- replace(list(longname = 2), nams, args)
}
do.call("lowlevel", args)
}
Here is a test:
wrapper()
longname = 1
wrapper(longname = 34)
longname = 34
wrapper(long = 34)
longname = 34
On 3/7/06, Duncan Murdoch <murdoch at stats.uwo.ca> wrote:
I'm writing wrappers for some functions that change some of the default
arguments. I'd rather not list all of the arguments for the low level
functions because there are about a dozen wrapper functions, and about
20 arguments to lowlevel. Instead I'm trying something like this:
lowlevel <- function(longname = 1) {
cat("longname = ", longname, "\n")
}
wrapper <- function(...) {
newargs <- list(longname = 2)
newargs[names(list(...))] <- list(...)
do.call("lowlevel", newargs)
}
This almost works:
> wrapper()
longname = 2
> wrapper(longname = 3)
longname = 3 But it fails if I try to use partial argument matching:
> wrapper(long=4)
Error in lowlevel(longname = 2, long = 4) :
unused argument(s) (long ...)
because long isn't matched to longname. Is there a reasonable way to do
this (e.g. using pmatch or charmatch) other than listing all the low
level arguments in the argument list to wrapper?
Duncan Murdoch
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
On 3/7/2006 9:42 AM, Gabor Grothendieck wrote:
Try this:
wrapper <- function(...) {
args <- list(...)
if (length(args)) {
nf <- names(formals(lowlevel))
nams <- nf[pmatch(names(args), nf)]
args <- replace(list(longname = 2), nams, args)
}
do.call("lowlevel", args)
}
Here is a test:
wrapper()
longname = 1
wrapper(longname = 34)
longname = 34
wrapper(long = 34)
longname = 34
Thanks, that's getting close, but it doesn't quite handle errors cleanly:
> wrapper(junk=3)
Error in lowlevel(longname = 2, "NA" = 3) :
unused argument(s) (NA ...)
It looks like I'll need something fairly elaborate.
Duncan Murdoch
On 3/7/06, Duncan Murdoch <murdoch at stats.uwo.ca> wrote:
I'm writing wrappers for some functions that change some of the default
arguments. I'd rather not list all of the arguments for the low level
functions because there are about a dozen wrapper functions, and about
20 arguments to lowlevel. Instead I'm trying something like this:
lowlevel <- function(longname = 1) {
cat("longname = ", longname, "\n")
}
wrapper <- function(...) {
newargs <- list(longname = 2)
newargs[names(list(...))] <- list(...)
do.call("lowlevel", newargs)
}
This almost works:
> wrapper()
longname = 2
> wrapper(longname = 3)
longname = 3 But it fails if I try to use partial argument matching:
> wrapper(long=4)
Error in lowlevel(longname = 2, long = 4) :
unused argument(s) (long ...)
because long isn't matched to longname. Is there a reasonable way to do
this (e.g. using pmatch or charmatch) other than listing all the low
level arguments in the argument list to wrapper?
Duncan Murdoch
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
The original code was not intended to be fully finished.
It was just to give the idea so I left out the error checking.
Adding such a check is just a matter of adding an if
statement to check the pmatch for NA:
wrapper <- function(...) {
args <- list(...)
if (length(args)) {
nf <- names(formals(lowlevel))
idx <- pmatch(names(args), nf)
if (any(is.na(idx)))
stop(paste("Invalid names used:", names(args)[is.na(idx)]))
nams <- nf[idx]
args <- replace(list(longname = 2), nams, args)
}
do.call("lowlevel", args)
}
wrapper(long = 3)
wrapper(junk = 5)
On 3/7/06, Duncan Murdoch <murdoch at stats.uwo.ca> wrote:
On 3/7/2006 9:42 AM, Gabor Grothendieck wrote:
Try this:
wrapper <- function(...) {
args <- list(...)
if (length(args)) {
nf <- names(formals(lowlevel))
nams <- nf[pmatch(names(args), nf)]
args <- replace(list(longname = 2), nams, args)
}
do.call("lowlevel", args)
}
Here is a test:
wrapper()
longname = 1
wrapper(longname = 34)
longname = 34
wrapper(long = 34)
longname = 34
Thanks, that's getting close, but it doesn't quite handle errors cleanly:
> wrapper(junk=3)
Error in lowlevel(longname = 2, "NA" = 3) :
unused argument(s) (NA ...)
It looks like I'll need something fairly elaborate.
Duncan Murdoch
On 3/7/06, Duncan Murdoch <murdoch at stats.uwo.ca> wrote:
I'm writing wrappers for some functions that change some of the default
arguments. I'd rather not list all of the arguments for the low level
functions because there are about a dozen wrapper functions, and about
20 arguments to lowlevel. Instead I'm trying something like this:
lowlevel <- function(longname = 1) {
cat("longname = ", longname, "\n")
}
wrapper <- function(...) {
newargs <- list(longname = 2)
newargs[names(list(...))] <- list(...)
do.call("lowlevel", newargs)
}
This almost works:
> wrapper()
longname = 2
> wrapper(longname = 3)
longname = 3 But it fails if I try to use partial argument matching:
> wrapper(long=4)
Error in lowlevel(longname = 2, long = 4) :
unused argument(s) (long ...)
because long isn't matched to longname. Is there a reasonable way to do
this (e.g. using pmatch or charmatch) other than listing all the low
level arguments in the argument list to wrapper?
Duncan Murdoch
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
Duncan Murdoch wrote:
I'm writing wrappers for some functions that change some of the default
arguments. I'd rather not list all of the arguments for the low level
functions because there are about a dozen wrapper functions, and about
20 arguments to lowlevel. Instead I'm trying something like this:
lowlevel <- function(longname = 1) {
cat("longname = ", longname, "\n")
}
wrapper <- function(...) {
newargs <- list(longname = 2)
newargs[names(list(...))] <- list(...)
do.call("lowlevel", newargs)
}
This almost works:
> wrapper()
longname = 2
> wrapper(longname = 3)
longname = 3 But it fails if I try to use partial argument matching:
> wrapper(long=4)
Error in lowlevel(longname = 2, long = 4) :
unused argument(s) (long ...)
because long isn't matched to longname. Is there a reasonable way to do
this (e.g. using pmatch or charmatch) other than listing all the low
level arguments in the argument list to wrapper?
Duncan Murdoch
If all you are doing is changing the default values of some arguments
this should work.
wrapper <- lowlevel
formals(wrapper) <- replace(formals(lowlevel), c("longname"), list(2))
Charles
Here's a slightly different approach:
lowlevel <- function(longname = 1, ...) {
cat("longname = ", longname, "\n")
}
wrapper <- function(...) {
newargs <- defaults(list(...), list(longname = 2))
do.call("lowlevel", newargs)
}
defaults <- function(x, defaults) {
if (length(x) == 0) return(defaults)
names(x) <- ifelse(is.na(pmatch(names(x), names(defaults))),
names(x), names(defaults))
c(x, defaults[setdiff(names(defaults), names(x))])
}
wrapper()
wrapper(longname=20)
wrapper(long=20)
wrapper(junk=3)
Hadley
On 3/7/2006 12:08 PM, Charles Dupont wrote:
Duncan Murdoch wrote:
I'm writing wrappers for some functions that change some of the default
arguments. I'd rather not list all of the arguments for the low level
functions because there are about a dozen wrapper functions, and about
20 arguments to lowlevel. Instead I'm trying something like this:
lowlevel <- function(longname = 1) {
cat("longname = ", longname, "\n")
}
wrapper <- function(...) {
newargs <- list(longname = 2)
newargs[names(list(...))] <- list(...)
do.call("lowlevel", newargs)
}
This almost works:
> wrapper()
longname = 2
> wrapper(longname = 3)
longname = 3 But it fails if I try to use partial argument matching:
> wrapper(long=4)
Error in lowlevel(longname = 2, long = 4) :
unused argument(s) (long ...)
because long isn't matched to longname. Is there a reasonable way to do
this (e.g. using pmatch or charmatch) other than listing all the low
level arguments in the argument list to wrapper?
Duncan Murdoch
If all you are doing is changing the default values of some arguments
this should work.
wrapper <- lowlevel
formals(wrapper) <- replace(formals(lowlevel), c("longname"), list(2))
Thanks for the suggestion, but the calculation of the new defaults is more involved than my example indicated. I really need to do some computation within the wrapper to come up with the new defaults. Duncan Murdoch
On 3/7/06, Duncan Murdoch <murdoch at stats.uwo.ca> wrote:
I'm writing wrappers for some functions that change some of the default
arguments. I'd rather not list all of the arguments for the low level
functions because there are about a dozen wrapper functions, and about
20 arguments to lowlevel. Instead I'm trying something like this:
lowlevel <- function(longname = 1) {
cat("longname = ", longname, "\n")
}
wrapper <- function(...) {
newargs <- list(longname = 2)
newargs[names(list(...))] <- list(...)
do.call("lowlevel", newargs)
}
This almost works:
> wrapper()
longname = 2
> wrapper(longname = 3)
longname = 3 But it fails if I try to use partial argument matching:
> wrapper(long=4)
Error in lowlevel(longname = 2, long = 4) :
unused argument(s) (long ...)
because long isn't matched to longname. Is there a reasonable way to do
this (e.g. using pmatch or charmatch) other than listing all the low
level arguments in the argument list to wrapper?
One trick I often use that is different from any of the suggestions I have seen so far (and is more transparent IMO) is the following:
lowlevel <- function(longname = 1) {
cat("longname = ", longname, "\n")
}
wrapper <- function(...) {
newArgs <-
function(longname = 2, ...)
list(longname = longname,
...)
do.call("lowlevel", newArgs(...))
}
which gives:
wrapper()
longname = 2
wrapper(longname = 3)
longname = 3
wrapper(long=20)
longname = 20
wrapper(junk=3)
Error in lowlevel(longname = 2, junk = 3) : unused argument(s) (junk ...) -Deepayan
On 3/7/2006 2:00 PM, Deepayan Sarkar wrote:
On 3/7/06, Duncan Murdoch <murdoch at stats.uwo.ca> wrote:
I'm writing wrappers for some functions that change some of the default
arguments. I'd rather not list all of the arguments for the low level
functions because there are about a dozen wrapper functions, and about
20 arguments to lowlevel. Instead I'm trying something like this:
lowlevel <- function(longname = 1) {
cat("longname = ", longname, "\n")
}
wrapper <- function(...) {
newargs <- list(longname = 2)
newargs[names(list(...))] <- list(...)
do.call("lowlevel", newargs)
}
This almost works:
> wrapper()
longname = 2
> wrapper(longname = 3)
longname = 3 But it fails if I try to use partial argument matching:
> wrapper(long=4)
Error in lowlevel(longname = 2, long = 4) :
unused argument(s) (long ...)
because long isn't matched to longname. Is there a reasonable way to do
this (e.g. using pmatch or charmatch) other than listing all the low
level arguments in the argument list to wrapper?
One trick I often use that is different from any of the suggestions I have seen so far (and is more transparent IMO) is the following:
Thanks, this is a nice idea. It looks as though it would combine well with Charles Duponts' suggestion to change the formals, i.e. I could have a generic version of your newArgs function, then change the formals and the body to match the pattern you used. One thing I'd like is to be able to put the new defaults in a list, because this code is going to show up in about a dozen places, and I don't want to have to edit all of them when the arg list of the low level function changes. So really I want something like newArgs(..., newDefaults) where newDefaults is a tagged list (e.g. list(longname = 2) for the example below), and the return value is a list containing all the names in newDefaults, perhaps with their values modified according to the args passed in ... . In the actual use newDefaults would be the result of a function call (the user will have made some configuration choices and I want those to be used as defaults to another function) but that's not so important here. I'd like the wrapper to be a bit like par(), though I notice that par() doesn't accept partial name matching so maybe I'm worrying about something I shouldn't. Duncan Murdoch
lowlevel <- function(longname = 1) {
cat("longname = ", longname, "\n")
}
wrapper <- function(...) {
newArgs <-
function(longname = 2, ...)
list(longname = longname,
...)
do.call("lowlevel", newArgs(...))
}
which gives:
wrapper()
longname = 2
wrapper(longname = 3)
longname = 3
wrapper(long=20)
longname = 20
wrapper(junk=3)
Error in lowlevel(longname = 2, junk = 3) : unused argument(s) (junk ...) -Deepayan
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
Okay, here's my effort based on Deepayan's and Charles' ideas. The
newArgs function is not what I'd call transparent, but I like the way
the wrapper looks.
> newArgs <- function(..., Params) {
+ f <- function(...) list(...)
+ formals(f) <- c(Params, formals(f))
+ b <- as.list(body(f))
+ body(f) <- as.call(c(b[1], names, b[-1]))
+ f(...)
+ }
>
> lowlevel <- function(longname = 1) {
+ cat("longname = ", longname, "\n")
+ }
>
> newDefaults <- list(longname=2)
>
> wrapper <- function (...)
+ do.call("lowlevel", newArgs(..., Params=newDefaults))
newArgs sets up f to look like
function (longname = 2, ...) list(longname = longname, ...)
and then calls it. The thing I like about this, as opposed to using
pmatch, is that I'm sure the partial matching is what's used by R's
argument matching, whereas that's only pretty likely with pmatch.
I also sort of like these lines:
+ names <- as.list(names(Params))
+ names(names) <- names
+ names <- lapply(names, as.name)
but maybe I should have named Params as names, so they looked like this:
+ names <- as.list(names(names))
+ names(names) <- names
+ names <- lapply(names, as.name)
And of course I like the fact that this seems to work, but we've seen
several versions that do that:
> wrapper()
longname = 2
> wrapper(longname=3)
longname = 3
> wrapper(long=3)
longname = 3
> wrapper(long=20)
longname = 20
> wrapper(junk=20)
Error in lowlevel(longname = 2, junk = 20) :
unused argument(s) (junk ...)
Duncan Murdoch
Whoops, just noticed that I cut when I should have copied. The newArgs
function should look like this:
newArgs <- function(..., Params) {
f <- function(...) list(...)
formals(f) <- c(Params, formals(f))
names <- as.list(names(Params))
names(names) <- names
names <- lapply(names, as.name)
b <- as.list(body(f))
body(f) <- as.call(c(b[1], names, b[-1]))
f(...)
}
Duncan Murdoch
On 3/7/2006 9:18 PM, Duncan Murdoch wrote:
Okay, here's my effort based on Deepayan's and Charles' ideas. The newArgs function is not what I'd call transparent, but I like the way the wrapper looks.
> newArgs <- function(..., Params) {
+ f <- function(...) list(...) + formals(f) <- c(Params, formals(f)) + b <- as.list(body(f)) + body(f) <- as.call(c(b[1], names, b[-1])) + f(...) + }
>
> lowlevel <- function(longname = 1) {
+ cat("longname = ", longname, "\n")
+ }
> > newDefaults <- list(longname=2) > > wrapper <- function (...)
+ do.call("lowlevel", newArgs(..., Params=newDefaults))
newArgs sets up f to look like
function (longname = 2, ...) list(longname = longname, ...)
and then calls it. The thing I like about this, as opposed to using
pmatch, is that I'm sure the partial matching is what's used by R's
argument matching, whereas that's only pretty likely with pmatch.
I also sort of like these lines:
+ names <- as.list(names(Params))
+ names(names) <- names
+ names <- lapply(names, as.name)
but maybe I should have named Params as names, so they looked like this:
+ names <- as.list(names(names))
+ names(names) <- names
+ names <- lapply(names, as.name)
And of course I like the fact that this seems to work, but we've seen
several versions that do that:
> wrapper()
longname = 2
> wrapper(longname=3)
longname = 3
> wrapper(long=3)
longname = 3
> wrapper(long=20)
longname = 20
> wrapper(junk=20)
Error in lowlevel(longname = 2, junk = 20) :
unused argument(s) (junk ...)
Duncan Murdoch
1 day later
Duncan Murdoch wrote:
Okay, here's my effort based on Deepayan's and Charles' ideas. The newArgs function is not what I'd call transparent, but I like the way the wrapper looks.
> newArgs <- function(..., Params) {
+ f <- function(...) list(...) + formals(f) <- c(Params, formals(f)) + b <- as.list(body(f)) + body(f) <- as.call(c(b[1], names, b[-1])) + f(...) + }
>
> lowlevel <- function(longname = 1) {
+ cat("longname = ", longname, "\n")
+ }
> > newDefaults <- list(longname=2) > > wrapper <- function (...)
+ do.call("lowlevel", newArgs(..., Params=newDefaults))
newArgs sets up f to look like
function (longname = 2, ...) list(longname = longname, ...)
and then calls it. The thing I like about this, as opposed to using
pmatch, is that I'm sure the partial matching is what's used by R's
argument matching, whereas that's only pretty likely with pmatch.
I also sort of like these lines:
+ names <- as.list(names(Params))
+ names(names) <- names
+ names <- lapply(names, as.name)
but maybe I should have named Params as names, so they looked like this:
+ names <- as.list(names(names))
+ names(names) <- names
+ names <- lapply(names, as.name)
And of course I like the fact that this seems to work, but we've seen
several versions that do that:
> wrapper()
longname = 2
> wrapper(longname=3)
longname = 3
> wrapper(long=3)
longname = 3
> wrapper(long=20)
longname = 20
> wrapper(junk=20)
Error in lowlevel(longname = 2, junk = 20) :
unused argument(s) (junk ...)
If while running the program you don't need to change either the default
options of the wrapper or the change which lowlevel function is called
this approach works well if you calculations are not too complicated.
createWrapper <- function(FUN, Params) {
as.function(c(replace(formals(FUN), names(Params), Params),
body(FUN)))
}
const <- 10
newDefaults <- alist(cat = 2,
longname = if(cat == 2){
if(!missing(dog)) cat + dog
else cat + 2
} else cat * const, dog=)
wrapper <- createWrapper(lowlevel, newDefaults)
> wrapper()
longname = 4
> wrapper(longname=3)
longname = 3
> wrapper(long=3)
longname = 3
> wrapper(3)
longname = 3
> wrapper(cat=4)
longname = 40
> wrapper(dog=6)
longname = 8
> wrapper(cat=4, dog=6)
longname = 40
> wrapper(long=3, cat=4, dog=6)
longname = 3
Charles