我很好奇R是否可以使用它的eval()函数来执行由字符串等提供的计算。

这是一个常见的情况:

eval("5+5")

然而,我得到的不是10:

[1] "5+5"

有解决方案吗?


当前回答

现在你也可以使用lazyeval包中的lazy_eval函数。

> lazyeval::lazy_eval("5+5")
[1] 10

其他回答

我同意有关于eval和parse的担忧,但如果表达式在字符串中,似乎没有什么可以做的。这个eval解析也被tidyverse专家在glue包中使用,参见https://github.com/tidyverse/glue/blob/d47d6c7701f738f8647b951df418cfd5e6a7bf76/R/transformer.R#L1-L12

也许eval(parse(text="5+5"))的可接受答案存在安全问题,如果文本字符串来自不受信任的来源,例如imagine user_input =" list.files()"或更糟糕的file.remove…

下面是一个潜在的解决方案。

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

类似地使用rlang:

eval(parse_expr("5+5"))

可以使用parse()函数将字符转换为表达式。你需要指定输入为文本,因为parse默认期望一个文件:

eval(parse(text="5+5"))

eval()函数计算一个表达式,但"5+5"是一个字符串,而不是表达式。使用parse() with text=<string>将字符串更改为表达式:

> eval(parse(text="5+5"))
[1] 10
> class("5+5")
[1] "character"
> class(parse(text="5+5"))
[1] "expression"

调用eval()会调用许多行为,其中一些行为并不明显:

> 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

参见tryCatch。

现在你也可以使用lazyeval包中的lazy_eval函数。

> lazyeval::lazy_eval("5+5")
[1] 10