评估作为字符串给出的表达式

Posted

技术标签:

【中文标题】评估作为字符串给出的表达式【英文标题】:Evaluate expression given as a string 【发布时间】:2010-12-17 03:41:08 【问题描述】:

我很想知道 R 是否可以使用它的eval() 函数来执行由例如提供的计算。一个字符串。

这是一种常见的情况:

eval("5+5")

但是,我得到的不是 10:

[1] "5+5"

有什么办法吗?

【问题讨论】:

尽管所有答案都显示了如何使用 parse 解决这个问题......为什么需要将语言类型存储在字符 string 中? Martin Mächler 的回答应该得到更多的支持。 谢谢@PetrMatousu。是的,我很震惊地看到错误信息现在是如何在 SO 上传播的……人们支持 eval(parse(text = *)) 虚假解决方案。 我想运行以下形式的脚本:QQ = c('11','12','13','21','22','23'),即:QQ =c(...,'ij',..) 其中 i,j 在可能不同的范围内变化从跑到跑。对于这个和类似的例子,我可以把脚本写成paste( "QQ = c('", paste(rep(1:2,each=3),1:3, sep="", collapse="','"), "')",sep=""),选项eval(parse(text=...))根据脚本在工作环境中创建向量QQ。如果不是使用“text=...”,那么正确的 R 编码器方法是什么? 【参考方案1】:

eval() 函数计算表达式,但 "5+5" 是字符串,而不是表达式。使用parse()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。

【讨论】:

正如 Shane 在下面指出的,“您需要指定输入是文本,因为 parse 默认需要一个文件” 应该指定使用 eval(parse) 的副作用。例如,如果您有一个预定义的变量 name 等于 "David",并且您使用 eval(parse(text = "name") == "Alexander" 重新分配,您将收到一个错误,因为eval & parse 不返回可以计算的 R 表达式。 @NelsonGon:使用quote()bquote()rlang 包提供的更复杂的工具构造的未计算表达式。 @ArtemSokolov 谢谢,我不知何故一直回到这个问题寻找替代方案。我看过rlang,但我发现最接近的是parse_expr,它调用parse_exprs,这又与使用parse并将其包装在eval中相同,这似乎与此处所做的相同.我不确定使用rlang 有什么好处。 @NelsonGon:使用rlang,您可以直接使用表达式,而不是字符串。无需解析步骤。它有两个优点。 1. 表达式操作总是会产生有效的表达式。字符串操作只会产生有效的字符串。在解析它们之前,您不会知道它们是否是有效的表达式。 2. 在字符串世界中没有等价于substitute() 类的函数,这严重限制了你操作函数调用的能力。考虑this glm wrapper。等价的字符串是什么样的?【参考方案2】:

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

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

【讨论】:

> fortunes::fortune("answer is parse") 如果答案是 parse() 你通常应该重新考虑这个问题。 -- Thomas Lumley R-help(2005 年 2 月)> @MartinMächler 这很讽刺,因为核心 R 包一直使用parse! github.com/wch/r-source/…【参考方案3】:

抱歉,我不明白为什么有太多人甚至认为字符串是可以评估的。你必须改变你的心态,真的。 忘记一侧的字符串与另一侧的表达式、调用、评估之间的所有连接。

(可能)唯一的连接是通过parse(text = ....),所有优秀的 R 程序员都应该知道,这很少是构造表达式(或调用)的有效或安全的方法。而是更多地了解substitute()quote(),以及可能使用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)

2017 年 12 月:好的,这是一个示例(在 cmets 中,没有很好的格式):

q5 <- quote(5+5)
str(q5)
# language 5 + 5

e5 <- expression(5+5)
str(e5)
# expression(5 + 5)

如果你有更多经验,你会发现q5"call"e5"expression",甚至e5[[1]]q5 相同:

identical(q5, e5[[1]])
# [1] TRUE

【讨论】:

你能举个例子吗?也许您可以向我们展示如何在 r 对象中“保持”到 5+5,然后使用引号和替换而不是字符和 eval(parse(text=)? 我可能有点失落。你什么时候得到10?或者这不是重点? @RichardDiSalvo:是的,上面的q5 &lt;- quote(5+5)表达式(实际上是“调用”)5+5,它是一个R对象,但不是字符串。您可以随时对其进行评估。再说一遍:使用,quote(),substitute(),... instead parse 直接创建调用或表达式,并且比 via parse(text= . ) 更有效。使用eval() 很好,使用parse(text=*) 容易出错,有时与构造调用和操作它们相比效率很低。@Nick S:在我们的运行示例中是eval(q5)eval(e5) @NickS :要获得 10,请评估调用/表达式,即对其调用 eval(.)。我的观点是人们不应该使用parse(text=.) 而应该使用quote(.) 等来构建稍后将是eval()ed 的调用。 eval(quote()) 在少数情况下确实有效,但在 eval(parse()) 可以正常工作的某些情况下会失败。【参考方案4】:

或者,您可以使用我的pander 包中的evals 来捕获输出和所有警告、错误和其他消息以及原始结果:

> 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 留下的漏洞;这使您的函数适合用于通过 mclapply 调用。我希望这个功能仍然存在! 谢谢你,@rpierce。这个函数最初是在 2011 年作为我们 rapport 包的一部分编写的,并且从那时起一直在积极维护,因为除了一些其他项目之外,我们的 rapporter.net 服务中也大量使用它——所以我相信它会保留维护了一段时间 :) 很高兴您发现它很有用,感谢您的友好反馈。【参考方案5】:

现在您还可以使用lazyeval 包中的lazy_eval 函数。

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

【讨论】:

【参考方案6】:

不知道为什么没有人专门提到两个 Base R 函数来执行此操作:str2lang()str2expression()。这些是parse() 的变体,但似乎更干净地返回表达式:

eval(str2lang("5+5"))

# > 10
  
eval(str2expression("5+5"))

# > 10

还想反击海报说任何试图这样做的人都是错误的。我正在读取存储为文件中文本的 R 表达式并尝试评估它们。这些函数非常适合这个用例。

【讨论】:

这并不是说它总是错,只是有很多很多情况下,以不同的方式做事会更安全、更好。【参考方案7】:

同样使用rlang:

eval(parse_expr("5+5"))

【讨论】:

来到这里寻找rlang 的答案,但如果有什么比基本替代品的优势呢?实际上,仔细检查所使用的代码表明它实际上是在使用eval(parse(....)),这是我想避免的。 不仅是那些负面的,而且它的名字也具有误导性。它不评估表达式。应该称为 parse_to_expr 或其他名称,以表明用户将知道它打算用于字符参数。

以上是关于评估作为字符串给出的表达式的主要内容,如果未能解决你的问题,请参考以下文章

从字符串评估数学表达式并将其插入堆栈

python以字符串形式评估命令

在一行中使用一个表达式两次 - 作为字符串格式的条件和?

如何取出包含数学表达式并给出结果的字符串的某些部分

R从表中提取公式并在函数中使用[关闭]

SQL 查询的正则表达式给出了一个空的 MatchCollection