I'm curious to know if R can use its eval()
function to perform calculations provided by e.g. a string.
This is a common case:
eval("5+5")
However, instead of 10 I get:
[1] "5+5"
Any solution?
I'm curious to know if R can use its eval()
function to perform calculations provided by e.g. a string.
This is a common case:
eval("5+5")
However, instead of 10 I get:
[1] "5+5"
Any solution?
The eval()
function evaluates an expression, but "5+5"
is a string, not an expression. Use parse()
with text=<string>
to change the string into an expression:
> eval(parse(text="5+5"))
[1] 10
> class("5+5")
[1] "character"
> class(parse(text="5+5"))
[1] "expression"
Calling eval()
invokes many behaviours, some are not immediately obvious:
> class(eval(parse(text="5+5")))
[1] "numeric"
> class(eval(parse(text="gray")))
[1] "function"
> class(eval(parse(text="blue")))
Error in eval(expr, envir, enclos) : object 'blue' not found
See also tryCatch.
quote()
, bquote()
, or the more sophisticated tools provided by the rlang
package. –
Mansour rlang
but the closest I found was parse_expr
which calls parse_exprs
which in turn is the same as using parse
and wrapping it in eval
which seems to be the same thing as done here. I am unsure what the advantage would be of using rlang
. –
Stringfellow rlang
, you would work directly with expressions, not strings. No parse step necessary. It has two advantages. 1. Expression manipulations will always produce valid expressions. String manipulations will only produce valid strings. You won't know if they are valid expressions until you parse them. 2. There's no equivalent to the substitute()
class of functions in the string world, which severely limits your ability to manipulate function calls. Consider this glm wrapper. What would a string equivalent look like? –
Mansour You can use the parse()
function to convert the characters into an expression. You need to specify that the input is text, because parse expects a file by default:
eval(parse(text="5+5"))
parse
all the time! github.com/wch/r-source/… –
Leatherneck Sorry but I don't understand why too many people even think a string was something that could be evaluated. You must change your mindset, really. Forget all connections between strings on one side and expressions, calls, evaluation on the other side.
The (possibly) only connection is via parse(text = ....)
and all good R programmers should know that this is rarely an efficient or safe means to construct expressions (or calls). Rather learn more about substitute()
, quote()
, and possibly the power of using do.call(substitute, ......)
.
fortunes::fortune("answer is parse")
# If the answer is parse() you should usually rethink the question.
# -- Thomas Lumley
# R-help (February 2005)
Dec.2017: Ok, here is an example (in comments, there's no nice formatting):
q5 <- quote(5+5)
str(q5)
# language 5 + 5
e5 <- expression(5+5)
str(e5)
# expression(5 + 5)
and if you get more experienced you'll learn that q5
is a "call"
whereas e5
is an "expression"
, and even that e5[[1]]
is identical to q5
:
identical(q5, e5[[1]])
# [1] TRUE
q5 <- quote(5+5)
above is the expression (actually the "call") 5+5
and it is an R object, but not a string. You can evaluate it any time. Again: using, quote(), substitute(), ... instead parse creates calls or expressions directly and more efficiently than via parse(text= . ). Using eval()
is fine, using parse(text=*)
is error prone and sometimes quite inefficient in comparison to construction calls and manipulating them.. @Nick S: It's eval(q5)
or eval(e5)
in our running example –
Mizzen eval(.)
on it. My point was that people should not use parse(text=.)
but rather quote(.)
etc, to construct the call which later will be eval()
ed. –
Mizzen eval(quote())
does work in a few cases but will fail for some cases where eval(parse())
would work well. –
Stringfellow eval(quote("5+10*2"))
returns "5+10*2"
whereas eval(parse(text="5+10*2"))
returns 25
. In this case, quote
doesn't seem to be the better function to use here... –
Architecture quote()
works when you give it a string. You should not have strings at all, when you need expressions or calls. Yes, sometimes your input does consist of strings, and you must parse these one way or the other, to turn them into expressions or calls which then you can evaluate. Using packages such as rlang
or lazy_eval
do the same thing: Always two steps: 1. parse (which does "string" -> expression) 2. eval (expression -> "value"). –
Mizzen eval(parse(text= * ))
(or any other variants of that). –
Mizzen Not sure why no one has mentioned two Base R functions specifically to do this: str2lang()
and str2expression()
. These are variants of parse()
, but seem to return the expression more cleanly:
eval(str2lang("5+5"))
# > 10
eval(str2expression("5+5"))
# > 10
Also want to push back against the posters saying that anyone trying to do this is wrong. I'm reading in R expressions stored as text in a file and trying to evaluate them. These functions are perfect for this use case.
Nowadays you can also use lazy_eval
function from lazyeval
package.
> lazyeval::lazy_eval("5+5")
[1] 10
Alternatively, you can use evals
from my pander
package to capture output and all warnings, errors and other messages along with the raw results:
> pander::evals("5+5")
[[1]]
$src
[1] "5 + 5"
$result
[1] 10
$output
[1] "[1] 10"
$type
[1] "numeric"
$msg
$msg$messages
NULL
$msg$warnings
NULL
$msg$errors
NULL
$stdout
NULL
attr(,"class")
[1] "evals"
evaluate::evaluate
by actually returning the result object; that leaves your function suitable for use for calling via mclapply. I hope that feature remains! –
Advowson rapport
package, and have been actively maintained since then as being heavily used in our rapporter.net service besides a few other projects as well -- so I'm sure it will remain maintained for a while :) I'm glad you find it useful, thanks for your kind feedback. –
Cacuminal Similarly using rlang
:
eval(parse_expr("5+5"))
rlang
answer but what if any is the advantage of this over base alternatives? Actually, close examination of the code used shows that it is in fact using eval(parse(....))
which I wanted to avoid. –
Stringfellow I agree there are concerns around eval and parse, but if the expression is in a string, there appears nothing much that can be done. This eval parse is also used in the glue package by the tidyverse experts, see https://github.com/tidyverse/glue/blob/d47d6c7701f738f8647b951df418cfd5e6a7bf76/R/transformer.R#L1-L12
Perhaps the accepted answer of eval(parse(text="5+5"))
has security concerns if the text string is from an untrusted source, eg imagine user_input = "list.files()"
or worse file.remove...
One potential work around is below.
The idea is to set the R environment in which the expression is to be evaluated. In R, most functions that 'comes with R' are actually in packages that gets autoloaded at R start up, eg 'list.files', 'library' and 'attach' functions come from the 'base' package. By setting the evaluation environment to empty environment, these functions are no longer available to the expression to be evaluated, preventing malicious code from executing. In the code below, by default I include only arithmetic related functions, otherwise user can provide the evaluation environment with explicitly allowed functions.
eval_text_expression <- function(text_expression, data_list, eval_envir = NULL) {
# argument checks
stopifnot(is.character(text_expression) && length(text_expression) == 1)
stopifnot(is.list(data_list))
stopifnot(length(data_list) == 0 || (!is.null(names(data_list)) && all(names(data_list) != "")))
stopifnot(all(!(lapply(data_list, typeof) %in% c('closure', 'builtin'))))
stopifnot(is.null(eval_envir) || is.environment(eval_envir))
# default environment for convenience
if (is.null(eval_envir)) {
arithmetic_funcs <- list("+" = `+`, "-" = `-`, "*" = `*`, "/" = `/`, "^" = `^`, "(" = `(`)
eval_envir = rlang::new_environment(data = arithmetic_funcs, parent = rlang::empty_env())
}
# load data objects into evaluation environment, then evaluate expression
eval_envir <- list2env(data_list, envir = eval_envir)
eval(parse(text = text_expression, keep.source = FALSE), eval_envir)
}
eval_text_expression("(a+b)^c - d", list(a = 1, b = 2, c = 3, d = 4))
# [1] 23
eval_text_expression("list.files()", list())
# Error in list.files() : could not find function "list.files"
eval_text_expression("list.files()", list(), eval_envir = rlang::new_environment(list("list.files" = list.files)))
# succeeds in listing my files if i explicitly allow it
© 2022 - 2024 — McMap. All rights reserved.
string
? Martin Mächler's answer should deserve much more upvotes. – Accountancyeval(parse(text = *))
fake solutions. – MizzenQQ = c('11','12','13','21','22','23')
, i.e.: QQ =c(...,'ij',..) with i,j varying on a range that is may vary from run to run. For this and similar examples, I can write the script aspaste( "QQ = c('", paste(rep(1:2,each=3),1:3, sep="", collapse="','"), "')",sep="")
, and the optioneval(parse(text=...))
creates the vector QQ in the working environment as per the script. What would be the proper R coder way to do this, if not with "text=..."? – Budwortheval(parse(text = "5+5"))
a "fake solution"? It seems to work fine for all the cases I have tried. There are reasons that one might need to evaluate something read-in as a string. I am finding your answer more confusing and less useful to evaluating a string (OP) than the others, which might be why the other answers have more upvotes? – Architecturei <- rep(1:2, each=3) ; j <- 1:3
the proper solution is (many times faster and more readable)QQ <- paste0(i, j)
-- voilà, that's all. – Mizzen