Valuta l'espressione fornita come una stringa


283

Sono curioso di sapere se R può usare la sua eval()funzione per eseguire calcoli forniti ad esempio da una stringa.

Questo è un caso comune:

eval("5+5")

Tuttavia, invece di 10 ottengo:

[1] "5+5"

Qualche soluzione?


6
Nonostante tutte le risposte che mostrano come risolverlo con l'analisi ... Perché è necessario memorizzare i tipi di lingua in un personaggio string? La risposta di Martin Mächler dovrebbe meritare molti più voti.
Petr Matousu,

7
Grazie @PetrMatousu. Sì, sono scioccato nel vedere come le informazioni errate sono diffuse su SO ora .. da parte di persone che hanno votato eval(parse(text = *)) soluzioni false.
Martin Mächler,

2
Voglio eseguire script del modulo:, QQ = c('11','12','13','21','22','23')ovvero: QQ = c (..., 'ij', ..) con i, j che variano su un intervallo che può variare da corsa a corsa. Per questo ed esempi simili, posso scrivere lo script come paste( "QQ = c('", paste(rep(1:2,each=3),1:3, sep="", collapse="','"), "')",sep="")e l'opzione eval(parse(text=...))crea il QQ vettoriale nell'ambiente di lavoro secondo lo script. Quale sarebbe il modo di codificatore R corretto per farlo, se non con "text = ..."?
VictorZurkowski,

Risposte:


419

La eval()funzione valuta un'espressione, ma "5+5"è una stringa, non un'espressione. Utilizzare parse()con text=<string>per modificare la stringa in un'espressione:

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

La chiamata eval()invoca molti comportamenti, alcuni non immediatamente evidenti:

> 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

Vedi anche tryCatch .


27
Come osserva Shane di seguito, "Devi specificare che l'input è testo, perché l'analisi prevede un file per impostazione predefinita"
PatrickT

1
gli effetti collaterali dell'uso di eval (parse) devono essere specificati. Ad esempio, se hai un nome di variabile predefinito uguale a "David" e riassegni usando eval (parse (text = "name") == "Alexander", otterrai un errore perché eval & parse non restituiscono un Espressione R che può essere valutata
Crt

1
@NelsonGon: espressioni non valutate costruite utilizzando quote(), bquote()o gli strumenti più sofisticati forniti dal rlangpacchetto.
Artem Sokolov,

@ArtemSokolov Grazie, in qualche modo continuo a tornare a questa domanda in cerca di un'alternativa. Ho guardato rlangma il più vicino che ho trovato era parse_exprquali chiamate parse_exprsche a loro volta sono le stesse dell'uso parsee del wrapping in evalcui sembra essere la stessa cosa fatta qui. Non sono sicuro di quale sarebbe il vantaggio di utilizzare rlang.
NelsonGon,

1
@NelsonGon: con rlang, lavoreresti direttamente con le espressioni, non con le stringhe. Non è necessario alcun passo di analisi. Ha due vantaggi. 1. Le manipolazioni delle espressioni produrranno sempre espressioni valide. Le manipolazioni delle stringhe produrranno solo stringhe valide. Non saprai se sono espressioni valide finché non le analizzi. 2. Non esiste un equivalente della substitute()classe di funzioni nel mondo delle stringhe, che limita fortemente la tua capacità di manipolare le chiamate di funzione. Considera questo involucro glm . Come sarebbe un equivalente di stringa?
Artem Sokolov,

100

È possibile utilizzare la parse()funzione per convertire i caratteri in un'espressione. È necessario specificare che l'input è testo, poiché l'analisi prevede un file per impostazione predefinita:

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

7
> fortune :: fortune ("la risposta è analisi") Se la risposta è analisi () di solito dovresti ripensare la domanda. - Thomas Lumley R-help (febbraio 2005)>
Martin Mächler,

13
@ MartinMächler È ironico, perché i pacchetti R di base usano parsesempre! github.com/wch/r-source/…
geneorama

49

Scusate ma non capisco perché troppe persone pensano addirittura che una stringa sia qualcosa che può essere valutata. Devi cambiare la tua mentalità, davvero. Dimentica tutte le connessioni tra stringhe da un lato e espressioni, chiamate, valutazione dall'altro lato.

L'unica (possibilmente) connessione è via parse(text = ....)e tutti i bravi programmatori R dovrebbero sapere che questo raramente è un mezzo efficiente o sicuro per costruire espressioni (o chiamate). Piuttosto, scopri di più su substitute(), quote()e possibilmente il potere di usare 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)

Dic.2017: Ok, ecco un esempio (nei commenti, non c'è una buona formattazione):

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

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

e se diventi più esperto imparerai che q5è un "call"considerando che e5è un "expression", e anche che e5[[1]]è identico a q5:

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

4
potresti fare un esempio? forse potresti mostrarci come "aggrapparci" a 5 + 5 in un oggetto r, quindi valutarlo in un secondo momento, usando la citazione e la sostituzione anziché un carattere e una valutazione (analisi (testo =)?
Richard DiSalvo

3
Potrei essere un po 'perso. A che punto ne ricevi 10? O non è questo il punto?
Nick S,

@RichardDiSalvo: sì, q5 <- quote(5+5)sopra c'è l'espressione (in realtà la "chiamata") 5+5ed è un oggetto R, ma non una stringa. Puoi valutarlo in qualsiasi momento. Ancora una volta: usando, quote (), substitute (), ... invece l' analisi crea chiamate o espressioni direttamente e in modo più efficiente rispetto all'analisi (testo =.). Usare eval()va bene, usare parse(text=*)è soggetto a errori e talvolta abbastanza inefficiente rispetto alle chiamate di costruzione e alla loro manipolazione. @Nick S: è eval(q5) o eval(e5) nel nostro esempio corrente
Martin Mächler

@NickS: per ottenere 10, si valuta la chiamata / espressione, ovvero si chiama eval(.)su di essa. Il mio punto era che le persone non dovevano usare, parse(text=.)ma piuttosto quote(.)ecc., Per costruire la chiamata che in seguito verrà modificata eval().
Martin Mächler,

2
eval(quote())funziona in alcuni casi, ma fallirà per alcuni casi in cui eval(parse())funzionerebbe bene.
NelsonGon,

18

In alternativa, è possibile utilizzare evalsdal mio panderpacchetto per acquisire l'output e tutti gli avvisi, errori e altri messaggi insieme ai risultati non elaborati:

> 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
Bella funzione; riempie un buco lasciato da evaluate::evaluaterestituendo effettivamente l'oggetto risultato; che lascia la tua funzione adatta all'uso per le chiamate tramite mclapply. Spero che questa funzione rimanga!
Russellpierce,

Grazie, @rpierce. Questa funzione è stata originariamente scritta nel 2011 come parte del nostro rapportpacchetto e da allora è stata attivamente mantenuta come ampiamente utilizzata nel nostro servizio rapporter.net oltre ad alcuni altri progetti, quindi sono sicuro che rimarrà mantenuta per un mentre :) Sono contento che lo trovi utile, grazie per il tuo gentile feedback.
daroczig,


2

Allo stesso modo usando rlang:

eval(parse_expr("5+5"))

3
Sono venuto qui alla ricerca di una rlangrisposta, ma se ci fosse il vantaggio di questo rispetto alle alternative di base? In realtà, un attento esame del codice utilizzato mostra che è in realtà l'utilizzo eval(parse(....))che volevo evitare.
NelsonGon,

4
Non solo quei negativi, ma il suo nome è anche fuorviante. NON sta valutando un'espressione. Dovrebbe essere chiamato parse_to_expr ot qualcos'altro per indicare che l'utente saprà che era destinato agli argomenti dei caratteri.
IRTFM,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.