Reputation: 87
I would like to use substitute()
to do variable substitutions inside expressions, without evaluating the expressions. For example, I have:
expr1 <- expression(if(x){
return(y)
} else {
return(z)
})
What I would like to do is get the following result assigned to "expr1":
expression(if(cos(x)){
return(y)
} else {
return(z)
})
By doing something like this:
expr2 <- substitute(expr1, list(x=cos(x)))
But that doesn't work, since substitute doesn't evaluate it's first argument. And so I just get back the original variable:
expr1
Now here it says that to do substitutions in an expression assigned to a variable, I have to execute substitute()
twice, followed by eval()
, like this:
expr2 <- eval(substitute(substitute(e, list(x = cos(x))), list(e = expr1)))
When I execute this statement, I do get an expression assigned to "expr2", but it doesn't contain the required substitution:
expression(if (x) {
return(y)
} else {
return(z)
})
What syntax can I apply to "expr1" so that I get the result I would like assigned to "expr2", namely:
expression(if (cos(x)) {
return(y)
} else {
return(z)
})
Upvotes: 5
Views: 3163
Reputation: 1672
An alternative might be:
x <- expr(cos(x))
expr2 <- expr(if (!!x) {
return(y)
} else {
return(z) })
Upvotes: 0
Reputation: 380
You have to add cos(x)
by wrapping it inside the x = quote()
function of the nested substitution()
ADDED: Per documentation, you can subset the expression using [
, [[
, or $
. You have to subset the expression object using [[
in the super substitute call. Try this:
expr2 <- eval(substitute(substitute(e, list(x=quote(cos(x)))), list(e = expr1[[1]])))
expr2
#if (cos(x)) {
# return(y)
#} else {
# return(z)
#}
To set expr2
as an expression, call: as.expression(expr2)
Upvotes: 0
Reputation: 206167
This appears to do the trick
do.call('substitute', list(expr1[[1]], list(x=quote(cos(x)))))
# if (cos(x)) {
# return(y)
# } else {
# return(z)
# }
First I use do.call
to pass the unevaluated expression as a parameter to substitute()
rather than a double-substitute
. Secondly, expression()
is really like a container object. You're really interested in just changing the code block of the first element of your expression. You could have avoided the expression()
and just used quote()
to avoid the "unboxing"
expr2 <- quote(if(x){ return(y) } else { return(z) })
do.call('substitute', list(expr2, list(x=quote(cos(x)))))
Both of these will return a class of type "if". If you need to, you can wrap it in an as.expression()
.
as.expression(do.call('substitute', list(expr1[[1]], list(x=quote(cos(x))))))
# expression(if (cos(x)) {
# return(y)
# } else {
# return(z)
# })
Upvotes: 7