Évalue l'expression donnée sous forme de chaîne

283

Je suis curieux de savoir si R peut utiliser sa eval()fonction pour effectuer des calculs fournis par exemple par une chaîne.

Ceci est un cas courant:

eval("5+5")

Cependant, au lieu de 10, j'obtiens:

[1] "5+5"

Toute solution?

Federico Giorgi
la source
6
Malgré toutes les réponses montrant comment résoudre ce problème avec l'analyse ... Pourquoi avez-vous besoin de stocker les types de langue dans un caractère string? La réponse de Martin Mächler devrait mériter beaucoup plus de votes positifs.
Petr Matousu
7
Merci @PetrMatousu. Oui, je suis choqué de voir comment les informations erronées sont diffusées sur SO maintenant .. par des gens qui votent pour de eval(parse(text = *)) fausses solutions.
Martin Mächler
2
Je veux exécuter des scripts de la forme:, QQ = c('11','12','13','21','22','23')c'est-à-dire: QQ = c (..., 'ij', ..) avec i, j variant sur une plage qui peut varier d'une exécution à l'autre. Pour cela et des exemples similaires, je peux écrire le script en tant que paste( "QQ = c('", paste(rep(1:2,each=3),1:3, sep="", collapse="','"), "')",sep=""), et l'option eval(parse(text=...))crée le vecteur QQ dans l'environnement de travail conformément au script. Quelle serait la bonne façon pour le codeur R de le faire, sinon avec "text = ..."?
VictorZurkowski

Réponses:

419

La eval()fonction évalue une expression, mais "5+5"est une chaîne, pas une expression. Utilisez parse()avec text=<string>pour changer la chaîne en une expression:

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

L'appel eval()appelle de nombreux comportements, certains ne sont pas immédiatement évidents:

> 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

Voir aussi tryCatch .

Harlan
la source
27
Comme Shane le note ci-dessous, "Vous devez spécifier que l'entrée est du texte, car parse attend un fichier par défaut"
PatrickT
1
les effets secondaires de l'utilisation d'eval (analyse) doivent être spécifiés. Par exemple, si vous avez un nom de variable prédéfini égal à "David" et que vous réaffectez en utilisant eval (parse (text = "name") == "Alexander", vous obtiendrez une erreur car eval & parse ne renvoie pas de Expression R qui peut être évaluée
Crt
1
@NelsonGon: expressions non évaluées construites à l'aide de quote(), bquote()ou des outils plus sophistiqués fournis par le rlangpackage.
Artem Sokolov
@ArtemSokolov Merci, je reviens en quelque sorte à cette question à la recherche d'une alternative. J'ai regardé, rlangmais le plus proche que j'ai trouvé était les parse_exprappels parse_exprsqui, à leur tour, sont les mêmes que l'utilisation parseet l'enveloppement, ce evalqui semble être la même chose que celle faite ici. Je ne sais pas quel serait l'avantage d'utiliser rlang.
NelsonGon
1
@NelsonGon: avec rlang, vous travailleriez directement avec des expressions, pas avec des chaînes. Aucune étape d'analyse nécessaire. Cela présente deux avantages. 1. Les manipulations d'expression produisent toujours des expressions valides. Les manipulations de chaînes ne produiront que des chaînes valides. Vous ne saurez pas si ce sont des expressions valides tant que vous ne les aurez pas analysées. 2. Il n'y a pas d'équivalent à la substitute()classe de fonctions dans le monde des chaînes, ce qui limite considérablement votre capacité à manipuler les appels de fonction. Considérez ce wrapper glm . À quoi ressemblerait une chaîne équivalente?
Artem Sokolov
100

Vous pouvez utiliser la parse()fonction pour convertir les caractères en une expression. Vous devez spécifier que l'entrée est du texte, car l'analyse attend un fichier par défaut:

eval(parse(text="5+5"))
Shane
la source
7
> fortunes :: fortune ("answer is parse") Si la réponse est parse (), vous devriez généralement repenser la question. - Thomas Lumley R-help (février 2005)>
Martin Mächler
13
@ MartinMächler C'est ironique, car les packages R principaux utilisent parsetout le temps! github.com/wch/r-source/…
geneorama
49

Désolé mais je ne comprends pas pourquoi trop de gens pensent même qu'une chaîne est quelque chose qui pourrait être évaluée. Vous devez vraiment changer votre état d'esprit. Oubliez toutes les connexions entre les chaînes d'un côté et les expressions, les appels et l'évaluation de l'autre côté.

La (éventuellement) seule connexion est via parse(text = ....)et tous les bons programmeurs R doivent savoir que c'est rarement un moyen efficace ou sûr de construire des expressions (ou des appels). Apprenez plutôt sur substitute(), quote()et peut-être sur la puissance de l'utilisation 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, voici un exemple (dans les commentaires, il n'y a pas de mise en forme sympa):

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

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

et si vous êtes plus expérimenté, vous apprendrez que q5c'est un "call"alors que e5c'est un "expression", et même cela e5[[1]]est identique à q5:

identical(q5, e5[[1]])
# [1] TRUE
Martin Mächler
la source
4
Pouvez-vous donner un exemple? peut-être pourriez-vous nous montrer comment "s'accrocher" à 5 + 5 dans un objet r, puis l'évaluer plus tard, en utilisant des guillemets et des substituts plutôt qu'un caractère et une évaluation (analyse (texte =)?
Richard DiSalvo
3
Je suis peut-être un peu perdu. À quel moment en avez-vous 10? Ou n'est-ce pas le but?
Nick S
@RichardDiSalvo: oui, q5 <- quote(5+5)au - dessus se trouve l'expression (en fait "l'appel") 5+5et c'est un objet R, mais pas une chaîne. Vous pouvez l'évaluer à tout moment. Encore une fois: utiliser, quote (), substitute (), ... à la place, l' analyse crée des appels ou des expressions directement et plus efficacement que via l'analyse (text =.). L'utilisation eval()est très bien, l'utilisation parse(text=*)est sujette aux erreurs et parfois assez inefficace par rapport aux appels de construction et les manipuler .. @ Nick S: C'est eval(q5) ou eval(e5) dans notre exemple en cours
Martin Mächler
@ NickS: pour obtenir 10, vous évaluez l'appel / l'expression, c'est-à-dire que vous l'appelez eval(.). Mon point était que les gens ne devraient pas utiliser parse(text=.)mais plutôt quote(.)etc, pour construire l'appel qui sera eval()édité plus tard .
Martin Mächler
2
eval(quote())fonctionne dans quelques cas mais échouera dans certains cas où eval(parse())cela fonctionnerait bien.
NelsonGon
18

Alternativement, vous pouvez utiliser à evalspartir de mon panderpackage pour capturer la sortie et tous les avertissements, erreurs et autres messages ainsi que les résultats bruts:

> 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"
Daroczig
la source
2
Belle fonction; remplit un trou laissé evaluate::evaluateen retournant réellement l'objet résultat; qui laisse votre fonction utilisable pour l'appel via mclapply. J'espère que cette fonctionnalité restera!
russellpierce
Merci, @rpierce. Cette fonction a été initialement écrite en 2011 dans le cadre de notre rapportpackage, et a été activement maintenue depuis lors comme étant fortement utilisée dans notre service rapporter.net en plus de quelques autres projets également - donc je suis sûr qu'elle restera maintenue pendant un tout :) Je suis heureux que vous le trouviez utile, merci pour vos aimables commentaires.
daroczig
14

De nos jours, vous pouvez également utiliser la lazy_evalfonction du lazyevalpackage.

> lazyeval::lazy_eval("5+5")
[1] 10
Paweł Kozielski-Romaneczko
la source
2

De même, en utilisant rlang:

eval(parse_expr("5+5"))
c1au61o_HH
la source
3
Je suis venu ici à la recherche d'une rlangréponse, mais quel est, le cas échéant, l'avantage de cette solution de base? En fait, un examen attentif du code utilisé montre qu'il est en fait d'utiliser eval(parse(....))ce que je voulais éviter.
NelsonGon
4
Non seulement ces points négatifs, mais son nom est également trompeur. Ce n'est PAS l'évaluation d'une expression. Doit être appelé parse_to_expr ou autre chose pour indiquer que l'utilisateur saura qu'il est destiné aux arguments de caractères.
IRTFM