Getting an R expression from a value (similar to enquote)
Asked Answered
Z

2

6

Assume I have a value x which is of some (unknown) type (especially: scalar, vector or list). I would like to get the R expression representing this value. If x == 1 then this function should simply return expression(1). For x == c(1,2)) this function should return expression(c(1,2)). The enquote function is quite near to that what I want, but not exactly.

By some playing around I found the following "solution" to my problem:

get_expr <- function(val) {
  tmp_expr <- enquote(val)
  tmp_expr[1] <- quote(expression())
  return(eval(tmp_expr))
}

get_expr(1) # returns expression(1)
get_expr(c(1, 2)) # returns expression(c(1, 2))
get_expr(list(x = 1)) # returns expression(list(x = 1))

But I think my get_expr function is some kind of hack. Logically, the evaluation should not be necessary.

Is there some more elegant way to do this? As far as I see, substitute does not really work for me, because the parameter of my get_expr function may be the result of an evaluation (and substitute(eval(expr)) does not do the evaluation).

I found another way via parse(text = deparse(val)), but this is even more a bad hack...

Zygotene answered 2/3, 2015 at 15:40 Comment(0)
A
6

as.expression(list(...)) seems to do it:

> get_expr <- function(val) as.expression(list(val))
> str(get_expr(1))
  expression(1)
> str(get_expr(c(1, 2)))
  expression(c(1, 2))
> str(get_expr(list(x=1)))
  expression(list(x = 1))
> val <- list(x=1, y=2)
> str(get_expr(val))
  expression(list(x = 1, y = 2))
Amharic answered 2/3, 2015 at 15:48 Comment(5)
Thanks a lot for this ideas, call('expression', val) looks much better and works for me and does not even need enquote. enquote(val)[[2]] does not work for me, because as.expression(enquote(val)[[2]]) returns expression(1, 2) (and a evaluation of this results in 2).Zygotene
EDIT: @PatrickRoocks, glad this helped. Not sure why you use as.expression; I don't suggest you do so. Re: your suggestion, note that it is pretty much equivalent to the as.call(list(...) version I had originally put at the end of the answer (now up front).Amharic
Well I just saw that this helps to get a oneliner - but I still need eval (i.e. eval(call('expression', val))). I want to use this result to substitute some part of another expression (i.e. expr[1] = get_expr(..)) without having a plenty of case distinctions. A object for which is.expression(x) == TRUE holds, makes life easier at this point. as.expression(c(1,2)) returns expression(1,2) and does not work for me.Zygotene
@PatrickRoocks, see update; let me know if this is still not what you want. Note the function actually returns expressions now, not language objects that need to be evaluated.Amharic
Thanks a lot, this is definitively the shortest and most elegant solution! Seems to fully exploit that expressions are internally represented as lists.Zygotene
K
2

You can use substitute(), and just need to call it a bit differently:

express <- function(e) substitute(expression(x), env = list(x=e))

v1 <- c(1, 2)
express(v1)
# expression(c(1, 2))

v2 <- list(a = 1, b = 2)
express(v2)
# expression(list(a = 1, b = 2))
Knuth answered 2/3, 2015 at 16:15 Comment(3)
Thanks a lot, this also more elegant than my idea. But still I have to eval it again to get an expression: is.call(v1) == TRUE and is.expression(v1) == FALSE. For eval(v1) it is the other way around and this is what works for me.Zygotene
@PatrickRoocks -- Yes indeed. express(v1) yields a call to the function expression(), while eval(express(v1)) returns the resulting expression object itself. That very subtle distinction (especially hard to grock since the printed output for both results is the same) is actually mentioned in this section of the R-lang manual. See especially the code block following the phrase "However, it does lead to the following bit of confusion:"Hagiographer
Glad you brought this back; meant to +1 before you deleted.Amharic

© 2022 - 2024 — McMap. All rights reserved.