[R] The evaluation of optional function arguments

Sietse Brouwer sbbrouwer+r-help at gmail.com
Mon Oct 20 03:34:39 CEST 2008


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



More information about the R-help mailing list