Reputation: 4145
args <- function() {
list(
x = "How old",
y = "is",
z = "the car?")
}
fun1 <- function(x = "How old", y = "is", z = "the car") {
# collect arguments
args_used <- as.list(match.call()[-1])
# Call fun2() using the args supplied by the user
do.call(fun2, args_used)
}
fun2 <- function(x = args()$x, y = args()$y, z = args()$z) {
paste(x, y, z)
}
Using fun1()
and/or fun2()
works if used directely, try e.g.,
> fun1("Where", "are", z = "the cars")
[1] "Where are the cars"
However, if I do:
list1 <- list(l1 = "Where", l2 = "Is this")
list2 <- list(l1 = "is", l2 = "the")
list3 <- list(l1 = "Peter", l2 = "new iPhone?")
mapply(function(i, j, k) fun1(x = i, y = j, z = k),
i = list1,
j = list2,
k = list3,
SIMPLIFY = FALSE)
I get
Error in paste(x, y, z) : object 'i' not found
I know why the problem happens and I even know how to fix it using eval(parse(text = ""))
(although I am aware of potential problems of the eval-parse strategy (see, e.g.: this discussion)
Therefore rewriting fun1()
like this:
fun1 <- function(x = "How old", y = "is", z = "the car") {
# collect arguments
args_used <- as.list(match.call()[-1])
args_used <- lapply(names(args_used), function(w) eval(parse(text = w)))
# Call fun2() using the args supplied by the user
do.call(fun2, args_used)
}
and subsequently using mapply(....)
as above works.
However here it becomes tricky.
w
in lapply(names(args_used), function(w) eval(parse(text = w)))
to x
(the same holds true for y
and z
. Now calling mapply
as above again, give:
$`l1` [1] "x is Peter" $l2 [1] "x the new iPhone?"
Clearly not what I wanted. Again I understand why this happens and a fix would be to not use
x
but something likew
but any name a pick is from now on reserved. Not ideal.
The approach also breaks if one of the arguments is called via the ...
argument. Modifying fun1()
like this:
fun1 <- function(x = "How old", y = "is", ...) {
# collect arguments
args_used <- as.list(match.call()[-1])
args_used <- lapply(names(args_used), function(x) eval(parse(text = x)))
# Call fun2() using the args supplied by the user
do.call(fun2, args_used)
}
now results in
Error in eval(parse(text = x)) : object 'z' not found
Again, I understand why (because the name "z"
is unknown within the fun1()
environement) but the question now is:
How do I solve this issue in a way that it avoids problem 1 and problem 2?
Upvotes: 0
Views: 111
Reputation: 206197
Just have your function evaluate the parameters in the calling environment. This seems to work (note the addition of envir=parent.frame()
).
fun1 <- function(x = "How old", y = "is", z = "the car") {
args_used <- as.list(match.call()[-1])
do.call(fun2, args_used, envir=parent.frame())
}
But I can't see why you are using lazy evaluation at all here. You can just capture all the arugments as part of the environment and pass along their values
fun1 <- function(x = "How old", y = "is", z = "the car") {
args_used <- as.list(environment())
# if you need ...: args_used <- c(as.list(environment()), list(...))
do.call(fun2, args_used)
}
Note that the environment()
will capture all variables that exist at the time you call it. So if you just want the variables that are passed in to your function, be sure to call it at the start of your function.
If your parameters have leading dots in their names, R normally treats these as "hidden" so you'll need to set the all.names=TRUE
parameter to the as.list.environment
function to get them. You can do
args_used <- as.list(environment(), all.names=TRUE)
Upvotes: 1