评估以字符串形式给出的表达式


283

我很好奇R是否可以使用其eval()功能来执行例如字符串提供的计算。

这是一个常见的情况:

eval("5+5")

但是,我得到的不是10个:

[1] "5+5"

有什么办法吗?


6
尽管所有答案都显示了如何使用解析来解决问题……为什么您需要将语言类型存储在字符中string?MartinMächler的答案应该得到更多的支持。
Petr Matousu '16

7
谢谢@PetrMatousu。是的,我很震惊地看到错误信息如何在SO ..上散布 eval(parse(text = *))
MartinMächler'17

2
我想运行以下形式的脚本: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编码器方法是什么?
VictorZurkowski

Answers:


417

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


27
正如Shane在下面指出的那样:“您需要指定输入为文本,因为默认情况下解析需要一个文件”
PatrickT 2014年

1
应该指定使用eval(parse)的副作用。例如,如果您具有一个等于“ David” 的预定义变量,并使用eval(parse(text =“ name”)==“ Alexander”重新分配,则会收到错误,因为eval和parse不会返回可以评估的R表达式
Crt

1
@NelsonGon:使用quote()bquote()rlang软件包提供的更复杂的工具构造的未求值表达式。
Artem Sokolov

@ArtemSokolov谢谢,我不知何故继续回到这个问题,寻找替代方案。我查看了一下,rlang但是发现的最接近的是parse_expr哪个调用parse_exprs,而调用又与使用parse和包装它eval相同,似乎和此处所做的是相同的。我不确定使用会有什么好处rlang
NelsonGon

1
@NelsonGon:使用rlang,您将直接使用表达式而不是字符串。无需解析步骤。它有两个优点。1.表达式操作将始终产生有效的表达式。字符串操作只会产生有效的字符串。在解析它们之前,您不会知道它们是否有效。2. substitute()字符串世界中没有相当于函数的类,这严重限制了您操纵函数调用的能力。考虑一下这个glm包装器。等效字符串是什么样的?
阿特姆·索科洛夫

99

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

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

7
> fortunes :: fortune(“答案为解析”)如果答案为parse(),则通常应重新考虑问题。-Thomas Lumley R-help(2005年2月)>
MartinMächler,

13
@具有讽刺意味的是,因为核心R包parse一直在使用!github.com/wch/r-source/...
geneorama

49

抱歉,但我不明白为什么太多人甚至认为字符串是可以评估的东西。确实,您必须改变观念。忘记一侧的字符串与另一侧的表达式,调用,求值之间的所有连接。

(可能是)唯一的连接是通过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月:好的,这是一个示例(注释中没有很好的格式):

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

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

而且,如果您有更多的经验,您将学到q5a 是,"call"而a e5"expression",甚至e5[[1]]等同于q5

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

4
你能举个例子吗?也许你可以告诉我们如何“等一等”,以5 + 5的R对象,然后再对其进行评估,使用引号和替代品,而不是一个字符和eval(解析(文本=)?
理查德DiSalvo

3
我可能会迷路。您什么时候得到10?还是那不是重点?
尼克S

@RichardDiSalvo:是的,q5 <- quote(5+5)上面表达式(实际上是“调用”)5+5,它是一个R对象,但不是字符串。您可以随时对其进行评估。再次:使用quote(),alternate(),... 代替 parse(text =。),parse直接且更有效地创建调用或表达式。使用 eval()是好的,使用parse(text=*)很容易出错,有时相较于建设呼唤相当低效和操纵他们.. @Nick S:这 eval(q5) 还是eval(e5) 在我们当前实例
马丁Mächler

@NickS:要获得10,请评估调用/表达式,即对其进行调用eval(.)。我的观点是,人们不应该使用parse(text=.)而是quote(.)构造诸如此类的呼叫,稍后再进行eval()编辑。
MartinMächler19年

2
eval(quote())在某些情况下确实可以工作,但在某些情况下eval(parse())会无法正常工作。
NelsonGon

18

另外,您也可以evals从我的pander程序包中捕获输出以及所有警告,错误和其他消息以及原始结果:

> 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"

2
功能不错;evaluate::evaluate通过实际返回结果对象来填充剩余的孔;使您的函数适合用于通过mclapply进行调用。我希望该功能仍然存在!
russellpierce 2015年

谢谢@rpierce。该功能最初是在2011年作为rapport程序包的一部分编写的,此后一直得到积极维护,除其他一些项目外,我们在rapporter.net服务中也得到了广泛使用-因此,我敢肯定,对于while :)很高兴您发现它很有用,谢谢您的反馈。
daroczig


2

同样使用rlang

eval(parse_expr("5+5"))

3
来到这里寻找rlang答案,但是相对于基本替代品,它的优点是什么?实际上,仔细检查所使用的代码表明,实际上eval(parse(....))我想避免使用它。
NelsonGon

4
不仅这些负面因素,而且其名称也具有误导性。它不评估表达式。应该称为parse_to_expr或其他名称,以指示用户将知道它打算用于字符参数。
IRTFM
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.