user3990797
user3990797

Reputation: 87

Using substitute to do variable substitutions inside R expressions

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

Answers (3)

Tom Greenwood
Tom Greenwood

Reputation: 1672

An alternative might be:

x <- expr(cos(x)) 

expr2 <- expr(if (!!x) {
                return(y)
                 } else {
                 return(z) })

Upvotes: 0

small_data88
small_data88

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

MrFlick
MrFlick

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

Related Questions