[R] The evaluation of optional function arguments

hadley wickham h.wickham at gmail.com
Mon Oct 20 04:53:46 CEST 2008


Why don't you want to do this?

timesDefineInside <- function(foo, bar...) {
   foo * bar
}

It seems like the obvious solution to your problem.

Hadley

On Sun, Oct 19, 2008 at 8:34 PM, Sietse Brouwer
<sbbrouwer+r-help at gmail.com> wrote:
> Dear R-helpers,
>
> I've got two functions; callTimes() calls times(), passing it an
> optional argument (bar) by name (bar=harry). times() then believes it
> has been passed a name, rather than a value — but I want the value,
> not the name.
> Worse, if I evaluate the name, it is evaluated in the environment
> times() was defined, not where it is called.
> How can I call times(), defining its optional argument as a variable,
> and have times() know the variable's value (at the moment of calling)?
>
> Below some code:
> (1) The basic case
> (2) A working kludge-around (but I'm still looking for the Right Way.)
> (3) A bunch of variants, so that you may get an idea of the behaviour involved.
>
> (3 starts from the simplest case and builds up from there. Think of it
> as background reading.)
>
> Actually, I'll put (1) up here:
>
> ########################
> ## (1) The basic case ##
>
> ## The calling function
> ## passes an optional argument (bar), as a variable (bar=harry).
> callTimes <- function(tom, harry) {
>    print(match.call(expand.dots=TRUE)) # callTimes(tom = 2, harry = 7)
>    timesDefineInside(foo=tom, bar=harry)
> }
>
> ## The called function
> ## does not explicitly ask for bar.
> timesDefineInside <- function(foo, ...) {
>    # Checks to ensure this code is only executed if bar is given.
>    print(match.call(expand.dots=TRUE))    # times(foo = tom, bar = harry)
>    bar <- match.call(expand.dots=TRUE)$bar
>    print(foo)                             # [1] 2
>    print(bar)                             # harry
>    print(mode(bar))                       # "name"
>    print(eval(bar))                       # [1] 13
>    foo*bar    # Error in foo * bar : non-numeric argument to binary operator
> }
>
> harry <- 13  # Now, let's see whether it thinks harry==13, or harry==7
> callTimes(2, 7)
> ## For the output, see the above inline comments.
>
> ## And THERE we have my problem. I have yet to find a way to call a function,
> ## give one of the optional arguments as argument=variable, and have it pick up
> ## on that variable's *value*, rather than its name. (It's not even the
> ## reference it picks up on: as you can see, if I evaluate the name, it uses
> ## the definition environment, rather than the calling environment.
>
> (For the work-around, scroll down.)
>
> A second question, not essential: I spent a while searching on this
> topic, and found myself unsure of the terminology. Tried a number of
> things, found nothing, now I'm not sure whether that's because I
> didn't know the right words. How would you phrase/define this problem?
>
> Kind regards, and thanks in advance,
>
> Sietse
> Sietse Brouwer
>
> #################
> ## Code below. ##
> ## Two comment-signs for comments, one for output, none for input.
>
> ##########################
> ## (2) A working kludge ##
>
> ## I can kludge around it by using
> callTimes <- function(tom, harry) {
>    timesArgs <- list(foo = tom, bar = harry)
>    do.call(times, timesArgs)
> }
> ## ; but is there a Right Way, too?
>
>
> ############################################################
> ## (3) Some variants, starting from the simplest case... ##
> ## ...and increasing in complexity.
>
>
> ## (3.1) This is the function I have trouble with: I can't get it to get bar
> ## from the arguments.
> times <- function(foo, ...) {
>    print(match.call(expand.dots=TRUE))
>    # Some checks and a guard statement.  You can safely assume that if there
>    # ain't no bar argument (bar fight?), this part will not be reached.
>    foo*bar
> }
>
> ## here we run it, and see that it gets bar not from
> ## the argument list, but from the defining environment.
> ls(bar)
> # Error in as.environment(pos) : no item called "bar" on the search list
> times(foo=2, bar=3)
> # times(foo = 2, bar = 3)
> # [1] Error in times(foo = 2, bar = 3): object "bar" not found
> ## Somehow, it doesn't cotton on to the fact that there's a bar
> ## in the argument list.
> bar <- 5
> times(foo=2, bar=3)
> # [1] 10
> ## Ah. it looks for bar in the environment where the function was defined, not
> ## in the one where it is evaluated. Lexical scoping, plus a rule of
> ## inheritance/who's-your-parent-now that I don't quite understand.
>
> ## (3.2) Now we try explicitly getting it from the argument list.
> rm(bar)
> timesDefineInside <- function(foo, ...) {
>    print(match.call(expand.dots=TRUE))
>    # again, imagine checks here.
>    bar <- match.call(expand.dots=TRUE)$bar
>    foo*bar
> }
> timesDefineInside(foo=7, bar=11)
> # [1] 77
>
> ## So this works, and all is well, nay? Nay. Turn thou to (1) to see what
> ## doth happen when we call timesDefineInside from inside another function.
>
> --
> Sietse Brouwer -- sbbrouwer at gmail.com -- +31 6 13456848
> Wildekamp 32 -- 6721 JD Bennekom -- the Netherlands
> MSN: sietse at gawab.com -- ICQ: 341232104
>
> ______________________________________________
> R-help at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
> and provide commented, minimal, self-contained, reproducible code.
>



-- 
http://had.co.nz/



More information about the R-help mailing list