2009-12-14 12 views
22

"Is 'eval' supposed to be nasty?" ispirato questo:Quando è "eval" in Ruby giustificato?

Per lo più tutti sono d'accordo che eval è male, e nella maggior parte dei casi non v'è più elegante/sostituzione più sicura.

Quindi volevo chiedere: se il eval viene utilizzato in modo improprio, spesso è davvero necessario come funzionalità del linguaggio? Sta facendo più male che bene?

Personalmente, l'unico posto che trovo utile è quello di interpolare stringhe fornite nel file di configurazione.

Modifica: L'intenzione di questa domanda è ottenere il maggior numero possibile di casi reali quando eval è l'unica soluzione o la migliore. Quindi, per favore, non andare in "dovrebbe una lingua limitare la creatività di un programmatore" direzione.

Edit2: E quando dico eval, ovviamente mi riferisco a eval ing stringa, non passando blocco rubino a instance_eval o class_eval.

risposta

20

L'unico caso che conosco (diverso da "Ho questa stringa e voglio eseguirlo") è che gestisce dinamicamente le variabili locali e globali. Ruby ha metodi per ottenere i nomi delle variabili locali e globali, ma non ha metodi per ottenere o impostare i loro valori in base a questi nomi. L'unico modo per fare AFAIK è con eval.

Qualsiasi altro utilizzo è quasi certamente sbagliato.Non sono un guru e non posso affermare categoricamente che non ce ne sono altri, ma ogni altro caso d'uso che abbia mai visto dove qualcuno ha detto "Hai bisogno di valutazione per questo", ho trovato una soluzione che non lo ha fatto.

Nota che sto parlando di eval stringa qui, a proposito. Ruby ha anche instance_eval, che può richiedere una stringa o un blocco da eseguire nel contesto del ricevitore. La forma a blocchi di questo metodo è veloce, sicura e molto utile.

+2

So che questo va oltre lo scopo della domanda principale, ma sono curioso di sapere in quali casi reali è necessario assegnare dinamicamente variabili locali/globali, cioè quando si usano attributi di istanza e metodi appropriati instance_variable_set e instance_variable_get non funziona ? –

+0

Bene, non si desidera sempre archiviare le cose oltre l'invocazione del metodo e talvolta si desidera utilizzare le variabili locali per determinare a quale oggetto viene impostato l'ivars. Un posto dove ciò può essere utile è eliminare la piastra di riscaldamento. Spesso un inizializzatore prende gli argomenti e li assegna a variabili di istanza con nome identico. Con 'eval', potresti abbreviarlo a qualcosa come' set_ivars (binding) '. – Chuck

+0

questo idioma è utile, e possibile solo con eval: 'eval ('self', block.binding)' – horseyguy

7

Il motivo della valutazione è perché quando ne hai bisogno, quando ne hai davvero bisogno, non ci sono sostituti. Dopotutto, c'è solo molto che puoi fare con l'invio di metodi creativi e, a un certo punto, devi eseguire codice arbitrario.

Solo perché una lingua ha una caratteristica che potrebbe essere pericolosa non significa che sia intrinsecamente una cosa negativa. Quando una lingua presume di sapere più del suo utente, è quando ci sono problemi.

Direi che quando trovi un linguaggio di programmazione privo di pericoli, ne hai trovato uno che non è molto utile.

Quando è giustificata la valutazione? In termini pragmatici, quando dici che lo è. Se è il tuo programma e tu sei il programmatore, imposti i parametri.

+3

L'unica cosa che potrei aggiungere è "è giustificato quando dici che lo è E sei consapevole delle ramificazioni". –

+0

La mia intenzione era quella di ottenere quanti più usi di valutazione giustificati possibili, per non discutere se l'autore di Ruby ha fatto la cosa giusta. Ma se vuoi andare lì, direi che spesso non sono l'unico a impostare i parametri, come dici tu, poiché spesso non sono l'unico programmatore del progetto. E eval è una delle caratteristiche linguistiche più pericolose, cadute in mani sbagliate. –

+1

-1. Le generalizzazioni e il rimettere la palla nel campo di MLaden non aiutano nessuno. – z5h

4

IMO principalmente per lingue specifiche del dominio.

"Evaluation Options in Ruby" è un articolo pubblicato da Jay Fields su InfoQ.

2

eval è uno strumento, non è né intrinsecamente buono né cattivo. È giustificato quando sei certo che sia lo strumento giusto per ciò che stai cercando di realizzare.

+0

Sono per lo più d'accordo. Il problema è quando quelli che abusano del martello di valutazione iniziano a rompere le cose solo perché non conoscono la lingua; come, riscrivono la RTL o qualcosa del genere. –

+0

@ The Wicked Flea: è per questo che ho incluso "ogni volta che sei sicuro".Se pensi che sia lo strumento giusto, non lo è. È solo quando sai che è lo strumento giusto e conosci le conseguenze di quella decisione che è davvero lo strumento giusto. –

+0

Beh, essere certi non giustifica automaticamente le tue decisioni. Inoltre, direi che alcuni strumenti vengono usati per scopi benigni più spesso di altri (un aratro e una bomba vengono in mente). Heh, un commento politico per una risposta politica. :( –

1

Uno strumento come eval riguarda la valutazione del codice in fase di esecuzione rispetto al tempo di "compilazione". Sai qual è il codice quando lanci Ruby? Allora probabilmente non hai bisogno di valutazione. Il tuo codice genera codice durante il runtime? allora probabilmente hai bisogno di valutarlo.

Ad esempio, i metodi/le funzioni necessarie in un parser decente ricorsivo dipendono dalla lingua che viene analizzata. Se la tua applicazione genera al volo un parser simile, allora potrebbe avere senso utilizzare eval. Potresti scrivere un parser generalizzato, ma potrebbe non essere una soluzione elegante.

"Programatically filling in a letrec in Scheme. Macros or eval?" è una domanda che ho postato su eval in Scheme, dove il suo uso è per lo più inevitabile.

+2

È possibile generare funzioni a runtime senza l'utilizzo di eval – Chuck

+0

Scrive codice che genera un insieme di funzioni reciprocamente ricorsive senza eval, quindi con eval La mia opinione è che il secondo esercizio sarà di solito più semplice – z5h

+0

In Ruby, il modo di generare metodi con e senza 'eval' è quasi la stessa cosa, quindi non sono sicuro del motivo per cui sarebbe diverso? – henrikhodne

12

Quando è giustificato? Direi quando non ci sono alternative ragionevoli. Sono stato in grado di pensare a un uso in cui non riesco a pensare ad un'alternativa: irb, che, se si scava abbastanza in profondità (a workspace.rb, intorno alla riga 80 nella mia copia, se sei interessato) usa eval per eseguire il tuo input:

def evaluate(context, statements, file = __FILE__, line = __LINE__) 
    eval(statements, @binding, file, line) 
end 

Questo sembra abbastanza ragionevole per me - una situazione in cui specificamente non si sa quale codice si sta andando ad avere per l'esecuzione fino al momento in cui ti viene chiesto di farlo. Qualcosa di dinamico e interattivo sembra adattarsi al conto.

5

C'è un caso d'uso molto importante per eval() che non può (AFAIK) essere ottenuto usando qualcos'altro, e cioè trovare il riferimento all'oggetto corrispondente per un'associazione.

Diciamo che sono stati passati un blocco, ma (per qualche motivo) è necessario accedere a opporsi contesto del legame, si dovrebbe effettuare le seguenti operazioni:

obj = eval('self', block.binding) 

E 'anche utile per definire quanto segue:

class Proc 
    def __context__ 
     eval('self', self.binding) 
    end 
end 
1

In generale eval è una funzionalità di lingua utile quando si desidera eseguire codice arbitrario. Questa dovrebbe essere una cosa rara, ma forse stai creando il tuo REPL o vuoi esporre il run-time rubino all'utente finale per qualche motivo. Potrebbe accadere ed è per questo che la funzione esiste. Se la stai usando per aggirare una parte della lingua (ad es. Le variabili globali), allora la lingua è imperfetta o la tua comprensione della lingua è imperfetta. Solitamente la soluzione non consiste nell'utilizzare eval ma per capire meglio la lingua o scegliere una lingua diversa.

Vale la pena notare che nel rubino in particolare instance_eval e class_eval hanno altri usi.