How to expand an ellipsis (...) argument without evaluating it in R - r

I need a function that accepts an arbitrary number of arguments and stores them in a variable as an expression without evaluating them. I managed to do it with match.call but it seems a little "kludgy".
foo <- function(...) {
expr <- match.call()
expr[[1]] <- expression
expr <- eval(expr)
# do some stuff with expr
return(expr)
}
> bla
Error: object 'bla' not found
> foo(x=bla, y=2)
expression(x = bla, y = 2)
Clarification
To clarify, I'm asking how to write a function that behaves like expression(). I can't use expression() directly for reasons that are too long to explain.

The most idiomatic way is:
f <- function(x, y, ...) {
match.call(expand.dots = FALSE)$`...`
}

Using . from plyr as a prototype
foo <- function (...)
{
as.expression(as.list(match.call()[-1]))
}

The ultimate intended outcome is slightly vague (could you clarify a bit?). However, this may be helpful:
foo2 <- function(...) {
expr <- as.list(substitute(list(...)))[-1L]
class(expr) <- "expression"
expr
}
example:
foo2(x=bla, y=2)
# expression(x = bla, y = 2)

Related

Testing an expression for missing-ness

I am doing some metaprogramming where I do depth-first traversal over a function body to implement partial-evaluation. Here, I am running into a problem when I encounter local variables. With those, I have to process pairlist objects and I want to process these depth-first. I am annotating expressions with attributes to propagate information.
A dummy-example of what I am doing could look like this:
dummy_expr_processing <- function(expr) {
attr(expr, "foo") <- "bar"
expr
}
dummy_pairlist_processing <- function(expr) {
for (i in seq_along(expr))
expr[[i]] <- dummy_expr_processing(expr[[i]])
}
which works fine for
f <- quote(function(x = 42, y = x) x + y)
dummy_pairlist_processing(f[[2]])
It breaks, however, for
f <- quote(function(x, y = x) x + y)
dummy_pairlist_processing(f[[2]])
because here, the expr is missing when I process x in the pair-list. I get the error
Error in dummy_expr_processing(expr[[i]]) :
argument "expr" is missing, with no default
Called from: dummy_expr_processing(expr[[i]])
I can try to get explicit about the argument
dummy_pairlist_processing <- function(expr) {
for (i in seq_along(expr))
expr[[i]] <- dummy_expr_processing(expr = expr[[i]])
}
dummy_pairlist_processing(f[[2]])
but that doesn't change anything.
I can also explicitly check if expr is missing
dummy_expr_processing <- function(expr) {
if (missing(expr)) return()
attr(expr, "foo") <- "bar"
expr
}
This will work if I call dummy_expr_processing without an argument but not if I call it with a "missing" expression. Then it is not considered missing by the missing() function but it certainly is considered missing by attr.
I can get around that by exploiting that missing data is represented as an empty symbol, so I can do this:
dummy_expr_processing <- function(expr) {
if (as.character(expr) == "") return()
attr(expr, "foo") <- "bar"
expr
}
dummy_pairlist_processing <- function(expr) {
for (i in seq_along(expr))
dummy_expr_processing(expr[[i]])
}
dummy_pairlist_processing(f[[2]])
and that will work, but notice that I no longer update expr[[i]] in dummy_pairlist_processing. I cannot do that if I letdummy_expr_processingreturnNULL`.
My questions now are:
Is as.character(expr) == "" the only way I can test if a variable holds an empty expression, or are there better ways?
Is there anyway I can also return the missing expression? Especially nice if I can return it and set an attribute on the returned object.

How can I tell if a function argument is given as an expression without evaluating it?

Here is a simple function that takes an expression as an argument.
f <- function(expr) {
expr <- substitute(expr)
eval(expr)
}
(In practice, I also need to manipulate the expression a little in the function and may evaluate the expression in some other environment where it is meaningful)
It works fine when we call
f(1+1)
by directly supplying an unevaluated argument.
However, I need f to also work like when I supply an explicitly defined expression outside, for example,
q <- quote(1+1)
f(q)
f(expr) needs to avoid substitute expr and return the value of the expression. However, the code above does not work because it results in the expression itself rather than 2.
So the question is: How can I tell if an argument is given as an expression without evaluating it in the current environment?
Perhaps you'd like to check if you're passed a "name" rather than a "call"
f <- function(expr) {
expr <- substitute(expr)
if(is.name(expr)) {
expr <- eval(expr, parent.frame())
}
eval(expr)
}
f(1+1)
# [1] 2
q<-quote(1+1)
f(q)
# [1] 2
There's probably better ways but here's one approach:
f <- function(expr) {
mess <- suppressWarnings(try(expr, silent = TRUE))
if (inherits(mess, "try-error")) {
expr <- substitute(expr)
}
eval(expr)
}
f(1+1)
## [1] 2
q <- quote(1+1)
f(q)
## [1] 2
This bit extends it to character vectors:
f <- function(expr) {
mess <- suppressWarnings(try(expr, silent = TRUE))
if (inherits(mess, "try-error")) {
expr <- substitute(expr)
}
if (is.character(expr)) return(eval(parse(text=expr)))
eval(expr)
}
f("1 + 1")
## [1] 2
I'd reccomend reading this bit HERE from Hadley's Advanced R

How to pass alternative arguments through wrapper function?

I would like to write a wrapper function for two functions that take optional arguments.
Here is an example of a function fun to wrap funA and funB
funA <- function(x = 1, y = 1) return(x+y)
funB <- function(z = c(1, 1) return(sum(z))
fun <- function(x, y, z)
I would like fun to return x+y if x and y are provided, and sum(z) if a vector z is provided.
I have tried to see how the lm function takes such optional arguments, but it is not clear exactly how, e.g., match.call is being used here.
After finding related questions (e.g. How to use R's ellipsis feature when writing your own function? and using substitute to get argument name with )), I have come up with a workable solution.
My solution has just been to use
fun <- function(...){
inputs <- list(...)
if (all(c("x", "y") %in% inputs){
ans <- funA(x, y)
} else if ("z" %in% inputs){
ans <- funB(z)
}
Is there a better way?
Note: Perhaps this question can be closed as a duplicate, but hopefully it can serve a purpose in guiding other users to a good solution: it would have been helpful to have expanded my search to variously include ellipsis, substitute, in addition to match.call.
Use missing. This returns funA(x, y) if both x and y are provided and returns funB if they are not but z is provided and if none of them are provided it returns NULL:
fun <- function(x, y, z) {
if (!missing(x) && !missing(y)) {
funA(x, y)
}
else if (!missing(z)) {
funB(z)
}
This seems to answer your question as stated but note that the default arguments in funA and funB are never used so perhaps you really wanted something different?
Note the fun that is provided in the question only works if the arguments are named whereas the fun here works even if they are provided positionally.
I would something like for example this using match.call. This is similar to your solution but more robust.
fun <- function(...){
arg <- as.list(match.call())[-1]
f <- ifelse(length(arg)>1,"funA","funB")
do.call(f,arg)
}
fun(x=1,y=2) ## or fun(1,2) no need to give named arguments
[1] 3
> fun(z=1:10) ## fun(1:10)
[1] 55

match.call called in wrong environment when eval’ing

I tried implementing a function let with the following semantics:
> let(x = 1, y = 2, x + y)
[1] 3
… which is conceptually somewhat similar to substitute with the syntax of with.
The following code almost works (the above invocation for instance works):
let <- function (...) {
args <- match.call(expand.dots = FALSE)$`...`
expr <- args[[length(args)]]
eval(expr,
list2env(lapply(args[-length(args)], eval), parent = parent.frame()))
}
Note the nested eval, the outer to evaluate the actual expression and the inner to evaluate the arguments.
Unfortunately, the latter evaluation happens in the wrong context. This becomes apparent when trying to call let with a function that examines the current frame, such as match.call:
> (function () let(x = match.call(), x))()
Error in match.call() :
unable to find a closure from within which 'match.call' was called
I thought of supplying the parent frame as the evaluating environment for eval, but that doesn’t work:
let <- function (...) {
args <- match.call(expand.dots = FALSE)$`...`
expr <- args[[length(args)]]
parent <- parent.frame()
eval(expr,
list2env(lapply(args[-length(args)], function(x) eval(x, parent)),
parent = parent)
}
This yields the same error. Which leads me to the question: how exactly is match.call evaluated? Why doesn’t this work? And, how do I make this work?
Will this rewrite solve your problem?
let <- function (expr, ...) {
expr <- match.call(expand.dots = FALSE)$expr
given <- list(...)
eval(expr, list2env(given, parent = parent.frame()))
}
let(x = 1, y = 2, x + y)
# [1] 3

Using "..." and "replicate"

In the documentation of sapply and replicate there is a warning regarding using ...
Now, I can accept it as such, but would like to understand what is behind it. So I've created this little contrived example:
innerfunction<-function(x, extrapar1=0, extrapar2=extrapar1)
{
cat("x:", x, ", xp1:", extrapar1, ", xp2:", extrapar2, "\n")
}
middlefunction<-function(x,...)
{
innerfunction(x,...)
}
outerfunction<-function(x, ...)
{
cat("Run middle function:\n")
replicate(2, middlefunction(x,...))
cat("Run inner function:\n")
replicate(2, innerfunction(x,...))
}
outerfunction(1,2,3)
outerfunction(1,extrapar1=2,3)
outerfunction(1,extrapar1=2,extrapar2=3)
Perhaps I've done something obvious horribly wrong, but I find the result of this rather upsetting. So can anyone explain to me why, in all of the above calls to outerfunction, I get this output:
Run middle function:
x: 1 , xp1: 0 , xp2: 0
x: 1 , xp1: 0 , xp2: 0
Run inner function:
x: 1 , xp1: 0 , xp2: 0
x: 1 , xp1: 0 , xp2: 0
Like I said: the docs seem to warn for this, but I do not see why this is so.
?replicate, in the Examples section, tells us explicitly that what you are trying to do does not and will not work. In the Note section of ?replicate we have:
If ‘expr’ is a function call, be aware of assumptions about where
it is evaluated, and in particular what ‘...’ might refer to. You
can pass additional named arguments to a function call as
additional named arguments to ‘replicate’: see ‘Examples’.
And if we look at Examples, we see:
## use of replicate() with parameters:
foo <- function(x=1, y=2) c(x,y)
# does not work: bar <- function(n, ...) replicate(n, foo(...))
bar <- function(n, x) replicate(n, foo(x=x))
bar(5, x=3)
My reading of the docs is that they do far more than warn you about using ... in replicate() calls; they explicitly document that it does not work. Much of the discussion in that help file relates to the ... argument of the other functions, not necessarily to replicate().
If you look at the code for replicate:
> replicate
function (n, expr, simplify = TRUE)
sapply(integer(n), eval.parent(substitute(function(...) expr)),
simplify = simplify)
<environment: namespace:base>
You see that the function is evaluated in the parent frame, where the ... from your calling function no longer exists.
There actually is a way to do this:
# Simple function:
ff <- function(a,b) print(a+b)
# This will NOT work:
testf <- function(...) {
replicate(expr = ff(...), n = 5)
}
testf(45,56) # argument "b" is missing, with no default
# This will:
testf <- function(...) {
args <- as.list(substitute(list(...)))[-1L]
replicate(expr = do.call(ff, args), n = 5)
}
testf(45,56) # 101
An alternative way to do that:
g <- function(x, y) x + y
f <- function(a = 1, ...) {
arg_list <- list(...)
replicate(n = 3, expr = do.call(g, args = arg_list))
}
f(x = 1, y = 2)

Resources