|
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 ______________________________________________ [hidden email] mailing list https://stat.ethz.ch/mailman/listinfo/r-devel |
|
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 <[hidden email]> 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 > > ______________________________________________ > [hidden email] mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel > ______________________________________________ [hidden email] 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 <[hidden email]> 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 >> >> ______________________________________________ >> [hidden email] mailing list >> https://stat.ethz.ch/mailman/listinfo/r-devel >> > > ______________________________________________ > [hidden email] mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel ______________________________________________ [hidden email] 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 <[hidden email]> 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 <[hidden email]> 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 > >> > >> ______________________________________________ > >> [hidden email] mailing list > >> https://stat.ethz.ch/mailman/listinfo/r-devel > >> > > > > ______________________________________________ > > [hidden email] mailing list > > https://stat.ethz.ch/mailman/listinfo/r-devel > > ______________________________________________ [hidden email] mailing list https://stat.ethz.ch/mailman/listinfo/r-devel |
|
In reply to this post by Duncan Murdoch
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 ______________________________________________ [hidden email] mailing list https://stat.ethz.ch/mailman/listinfo/r-devel |
|
In reply to this post by Duncan Murdoch
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 ______________________________________________ [hidden email] mailing list https://stat.ethz.ch/mailman/listinfo/r-devel |
|
In reply to this post by Charles Dupont
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 ______________________________________________ [hidden email] mailing list https://stat.ethz.ch/mailman/listinfo/r-devel |
|
In reply to this post by Duncan Murdoch
On 3/7/06, Duncan Murdoch <[hidden email]> 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 ______________________________________________ [hidden email] mailing list https://stat.ethz.ch/mailman/listinfo/r-devel |
|
On 3/7/2006 2:00 PM, Deepayan Sarkar wrote:
> > On 3/7/06, Duncan Murdoch <[hidden email]> 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 > > ______________________________________________ > [hidden email] mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel ______________________________________________ [hidden email] 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 ______________________________________________ [hidden email] mailing list https://stat.ethz.ch/mailman/listinfo/r-devel |
|
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 > ______________________________________________ [hidden email] mailing list https://stat.ethz.ch/mailman/listinfo/r-devel |
|
In reply to this post by Duncan Murdoch
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 ______________________________________________ [hidden email] mailing list https://stat.ethz.ch/mailman/listinfo/r-devel |
| Powered by Nabble | Edit this page |
