2009-12-14 5 views
22

Is 'eval' supposed to be nasty?“ inspirierte diese:Wann ist `eval` in Ruby gerechtfertigt?

Meistens alle einig, dass eval schlecht ist, und in den meisten Fällen gibt es elegantere/sicheren Ersatz.

Also wollte ich fragen: Wenn eval so oft missbraucht wird, ist es wirklich als Sprachfunktion benötigt? Tut es mehr böse als gut?

Persönlich ist der einzige Ort, den ich es nützlich finde, Strings in der Konfigurationsdatei interpolieren.

Edit: Die Absicht dieser Frage ist es, so viele reale Fälle wie möglich zu erhalten, wenn Eval die einzige oder die beste Lösung ist. Also, bitte, gehen Sie nicht auf "sollte eine Sprache die Kreativität eines Programmierers einschränken".

Edit2: Und wenn ich eval sagen, natürlich Ich beziehe mich auf eval Zeichenfolge ing, nicht rubin Block instance_eval oder class_eval vorbei.

Antwort

20

Der einzige Fall, den ich kenne (außer "Ich habe diese Zeichenfolge und ich möchte es ausführen") ist dynamisch mit lokalen und globalen Variablen. Ruby verfügt über Methoden zum Abrufen der Namen von lokalen und globalen Variablen, es fehlen jedoch Methoden zum Abrufen oder Festlegen ihrer Werte basierend auf diesen Namen. Die einzige Möglichkeit, AFAIK zu machen, ist mit eval.

Jede andere Verwendung ist fast sicher falsch.Ich bin kein Guru und kann nicht kategorisch sagen, dass es keine anderen gibt, aber bei jedem anderen Anwendungsfall, den ich je gesehen habe, wo jemand sagte: "Du brauchst Eval dafür", habe ich eine Lösung gefunden, die das nicht tut.

Beachten Sie, dass ich hier übrigens über string eval sprechen. Ruby hat auch instance_eval, die entweder eine Zeichenfolge oder einen Block zur Ausführung im Kontext des Empfängers nehmen kann. Die Blockform dieser Methode ist schnell, sicher und sehr nützlich.

+2

Ich weiß, dass dies über den Rahmen der primären Frage hinausgeht, aber ich bin gespannt, in welchen Fällen der tatsächlichen Verwendung Sie lokale/globale Variablen dynamisch zuweisen müssen, dh wenn Instanzattribute und die entsprechenden Methoden instance_variable_set und instance_variable_get nicht funktionieren ? –

+0

Nun, Sie möchten nicht immer Dinge hinter dem Methodenaufruf speichern, und manchmal möchten Sie lokale Variablen verwenden, um zu bestimmen, auf welche Ivars gesetzt wird. Ein Ort, an dem dies nützlich sein kann, ist das Entfernen von Standardwerten. Häufig wird ein Initialisierer Argumente entgegennehmen und sie den gleichnamigen Instanzvariablen zuweisen. Mit 'eval' könnten Sie dies zu etwas wie' set_ivars (binding) 'abkürzen. – Chuck

+0

Dieses Idiom ist nützlich und nur mit eval möglich: 'eval ('self', block.binding)' – horseyguy

7

Der Grund eval ist, weil, wenn Sie es brauchen, wenn Sie es wirklich brauchen, gibt es keine Ersatzstoffe. Es gibt schließlich nur so viel, was Sie mit dem kreativen Methoden-Dispatching tun können, und irgendwann müssen Sie beliebigen Code ausführen.

Nur weil eine Sprache eine Funktion hat, die gefährlich sein kann, bedeutet das nicht, dass sie von Natur aus eine schlechte Sache ist. Wenn eine Sprache mehr als ihren Benutzer kennt, dann gibt es Ärger.

Ich würde argumentieren, wenn Sie eine Programmiersprache ohne Gefahr finden, haben Sie eine gefunden, die nicht sehr nützlich ist.

Wann ist eval berechtigt? In pragmatischer Hinsicht, wenn Sie es sagen. Wenn es Ihr Programm ist und Sie der Programmierer sind, legen Sie die Parameter fest.

+3

Die einzige Sache, die ich hinzufügen könnte, ist "es ist gerechtfertigt, wenn Sie sagen, es ist und Sie sind sich der Auswirkungen bewusst". –

+0

Meine Absicht war, so viele gerechtfertigte Eval Verwendungen wie möglich zu bekommen, nicht zu streiten, ob Ruby Autor (en) das Richtige getan haben. Aber wenn Sie dorthin gehen wollen, würde ich sagen, dass ich oft nicht der einzige bin, der die Parameter setzt, wie Sie sagen, da ich oft nicht der einzige Programmierer am Projekt bin. Und Eval ist eines der gefährlichsten Sprachmerkmale, die in falsche Hände geraten sind. –

+1

-1. Verallgemeinerungen und das Zurücklegen des Balls in MLadens Gericht helfen niemandem. – z5h

4

IMO hauptsächlich für domänenspezifische Sprachen.

"Evaluation Options in Ruby" ist ein Artikel von Jay Fields darüber auf InfoQ.

2

Eval ist ein Werkzeug, es ist weder von Natur aus gut noch böse. Es ist gerechtfertigt, wenn Sie sicher sind, dass es das richtige Werkzeug für das ist, was Sie erreichen möchten.

+0

stimme ich größtenteils zu. Das Problem ist, wenn diejenigen, die den Eval-Hammer missbrauchen, anfangen, Dinge zu brechen, nur weil sie die Sprache nicht kennen; wie, schreiben sie die RTL oder so etwas neu. –

+0

@The Wicked Flea: Deshalb habe ich "wenn Sie sicher sind" aufgenommen.Wenn Sie denken, es ist das richtige Werkzeug, ist es nicht. Nur wenn Sie wissen, dass es das richtige Werkzeug ist und die Konsequenzen dieser Entscheidung kennen, ist es in der Tat das richtige Werkzeug. –

+0

Nun, wenn Sie sich sicher sind, werden Ihre Entscheidungen nicht automatisch gerechtfertigt. Außerdem würde ich argumentieren, dass einige Werkzeuge häufiger als andere für einen guten Zweck verwendet werden (ein Pflug und eine Bombe kommen mir in den Sinn). Heh, ein politischer Kommentar für eine politische Antwort. :( –

1

Ein Werkzeug wie Eval ist über die Evaluierung von Code zur Laufzeit gegen "Kompilierzeit". Weißt du, was der Code ist, wenn du Ruby startest? Dann brauchst du eval wahrscheinlich nicht. Generiert der Code während der Laufzeit Code? dann musst du es wahrscheinlich auswerten.

Zum Beispiel hängen die Methoden/Funktionen, die in einem rekursiven anständigen Parser benötigt werden, von der Sprache ab, die analysiert wird. Wenn Ihre Anwendung einen solchen Parser on-the-fly erstellt, kann es sinnvoll sein, eval zu verwenden. Sie könnten einen verallgemeinerten Parser schreiben, aber es ist vielleicht keine so elegante Lösung.

"Programatically filling in a letrec in Scheme. Macros or eval?" ist eine Frage, die ich über eval in Scheme gestellt habe, wo seine Verwendung meistens unvermeidlich ist.

+2

Sie können Funktionen erzeugen bei Laufzeit ohne Verwendung von eval. – Chuck

+0

Schreiben Sie Code, der eine Reihe von gegenseitig rekursiven Funktionen ohne eval generiert.Dann mit eval. Meine Behauptung ist, dass die zweite Übung in der Regel einfacher sein wird. – z5h

+0

In Ruby, die Methode Methoden mit und ohne 'eval' zu generieren ist fast gleich, also bin ich mir nicht sicher, warum das anders wäre? – henrikhodne

12

Wann ist das gerechtfertigt? Ich würde sagen, wenn es keine vernünftige Alternative gibt. Ich konnte mir eine Anwendung vorstellen, bei der ich mir keine Alternative vorstellen kann: irb, die, wenn Sie tief genug graben (bis workspace.rb, um die Zeile 80 in meiner Kopie, wenn Sie interessiert sind) eval verwendet, um Ihre Eingabe auszuführen:

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

das ziemlich vernünftig mir scheint - eine Situation, wo Sie speziell wissen nicht, was Code, den Sie bis zum Moment haben ausführen wirst, die Sie gefragt werden, dies zu tun. Etwas Dynamisches und Interaktives scheint der Rechnung zu entsprechen.

5

Es gibt einen sehr wichtigen Anwendungsfall für eval(), der nicht (AFAIK) mit etwas anderem erreicht werden kann, und das ist die entsprechende Objektreferenz für eine Bindung zu finden.

Sagen Sie bitte einen Block bestanden, aber (aus irgendeinem Grund) Sie Zugriff benötigen Kontext der Bindung an das Objekt, würden Sie wie folgt vor:

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

Es ist auch sinnvoll, die folgenden zu definieren:

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

Im Allgemeinen eval ist eine nützliche Sprachfunktion, wenn Sie beliebigen Code ausführen möchten. Dies sollte eine seltene Sache sein, aber vielleicht machen Sie Ihre eigene REPL oder Sie möchten die Ruby-Laufzeit aus irgendeinem Grund dem Endbenutzer zugänglich machen. Es könnte passieren und deshalb existiert das Feature. Wenn Sie es verwenden, um einen Teil der Sprache (z. B. globale Variablen) zu umgehen, dann ist entweder die Sprache fehlerhaft oder Ihr Verständnis der Sprache ist fehlerhaft. Die Lösung besteht normalerweise nicht darin, eval zu verwenden, sondern entweder die Sprache besser zu verstehen oder eine andere Sprache auszuwählen.

Es ist erwähnenswert, dass in Ruby besondere instance_eval und class_eval andere Verwendungen haben.