Ich weiß, dass Lisp- und Scheme-Programmierer normalerweise sagen, dass eval
dies vermieden werden sollte, es sei denn, dies ist unbedingt erforderlich. Ich habe die gleiche Empfehlung für mehrere Programmiersprachen gesehen, aber noch keine Liste klarer Argumente gegen die Verwendung von eval
. Wo finde ich einen Bericht über die möglichen Probleme bei der Verwendung eval
?
Zum Beispiel kenne ich die Probleme bei der GOTO
prozeduralen Programmierung (macht Programme unlesbar und schwer zu warten, macht Sicherheitsprobleme schwer zu finden usw.), aber ich habe nie die Argumente dagegen gesehen eval
.
Interessanterweise sollten dieselben Argumente gegen GOTO
Fortsetzungen gültig sein, aber ich sehe, dass Schemers zum Beispiel nicht sagt, dass Fortsetzungen "böse" sind - Sie sollten nur vorsichtig sein, wenn Sie sie verwenden. Es ist viel wahrscheinlicher, dass sie bei der Verwendung eval
von Code die Stirn runzeln als bei der Verwendung von Fortsetzungen (soweit ich sehen kann - ich könnte mich irren).
Antworten:
Es gibt mehrere Gründe, warum man nicht verwenden sollte
EVAL
.Der Hauptgrund für Anfänger ist: Sie brauchen es nicht.
Beispiel (unter der Annahme von Common Lisp):
BEWERTEN Sie einen Ausdruck mit verschiedenen Operatoren:
Das ist besser geschrieben als:
Es gibt viele Beispiele, bei denen Anfänger, die Lisp lernen, glauben
EVAL
, dass sie es brauchen , aber sie brauchen es nicht - da Ausdrücke ausgewertet werden und man auch den Funktionsteil bewerten kann. MeistensEVAL
zeigt die Verwendung von ein Unverständnis des Bewerters.Es ist das gleiche Problem mit Makros. Oft schreiben Anfänger Makros, in die sie Funktionen schreiben sollen - ohne zu verstehen, wofür Makros wirklich sind, und ohne zu verstehen, dass eine Funktion bereits die Aufgabe erfüllt.
Es ist oft das falsche Werkzeug für den Job
EVAL
und zeigt oft an, dass der Anfänger die üblichen Lisp-Bewertungsregeln nicht versteht.Wenn Sie denken , Sie brauchen
EVAL
, dann prüfen , ob so etwas wieFUNCALL
,REDUCE
oderAPPLY
könnte stattdessen verwendet werden.FUNCALL
- eine Funktion mit Argumenten aufrufen:(funcall '+ 1 2 3)
REDUCE
- Rufen Sie eine Funktion in einer Werteliste auf und kombinieren Sie die Ergebnisse:(reduce '+ '(1 2 3))
APPLY
- Rufen Sie eine Funktion mit einer Liste als Argument auf :(apply '+ '(1 2 3))
.F: Brauche ich wirklich eine Bewertung oder hat der Compiler / Evaluator bereits das, was ich wirklich will?
Die Hauptgründe
EVAL
für etwas fortgeschrittenere Benutzer zu vermeiden :Sie möchten sicherstellen, dass Ihr Code kompiliert wird, da der Compiler Code auf viele Probleme überprüfen kann und schnelleren Code generiert, manchmal VIEL VIEL VIEL (das ist Faktor 1000 ;-)) schnellerer Code
Code, der erstellt wurde und ausgewertet werden muss, kann nicht so früh wie möglich kompiliert werden.
Die Auswertung willkürlicher Benutzereingaben führt zu Sicherheitsproblemen
Einige Anwendungen der Evaluierung mit
EVAL
können zur falschen Zeit erfolgen und Build-Probleme verursachenUm den letzten Punkt mit einem vereinfachten Beispiel zu erklären:
Daher möchte ich möglicherweise ein Makro schreiben, das basierend auf dem ersten Parameter entweder
SIN
oder verwendetCOS
.(foo 3 4)
tut(sin 4)
und(foo 1 4)
tut(cos 4)
.Jetzt haben wir vielleicht:
Dies ergibt nicht das gewünschte Ergebnis.
Man kann dann das Makro reparieren,
FOO
indem man die Variable bewertet:Aber dann funktioniert das immer noch nicht:
Der Wert der Variablen ist zur Kompilierungszeit einfach nicht bekannt.
Ein allgemeiner wichtiger Grund zu vermeiden
EVAL
: Es wird oft für hässliche Hacks verwendet.quelle
eval
einfach zurück, weil sie nicht wissen, dass es eine bestimmte Sprach- oder Bibliotheksfunktion gibt, um das zu tun, was sie tun möchten. Ähnliches Beispiel von JS: Ich möchte eine Eigenschaft von einem Objekt unter Verwendung eines dynamischen Namens abrufen, also schreibe ich:eval("obj.+" + propName)
wann ich hätte schreiben könnenobj[propName]
."obj.+"
? Zuletzt habe ich überprüft,+
ist nicht gültig, wenn Punktreferenzen in JS verwendet werden.eval
(in jeder Sprache) ist nicht böse wie eine Kettensäge nicht böse. Es ist ein Werkzeug. Es ist zufällig ein mächtiges Werkzeug, das bei Missbrauch Gliedmaßen durchtrennen und ausweiden kann (bildlich gesprochen), aber das Gleiche gilt für viele Werkzeuge in der Toolbox eines Programmierers, einschließlich:goto
und FreundeWenn Sie eines dieser leistungsstarken, potenziell gefährlichen Tools verwenden müssen, fragen Sie sich dreimal: "Warum?" in einer Kette. Beispielsweise:
Wenn Sie am Ende dieser Kette angelangt sind und das Werkzeug immer noch so aussieht, als wäre es das Richtige, dann tun Sie es. Dokumentieren Sie die Hölle daraus. Testen Sie die Hölle daraus. Überprüfen Sie die Richtigkeit und Sicherheit immer und immer wieder. Aber mach es.
quelle
Eval ist in Ordnung, solange Sie genau wissen was darin . Alle Benutzereingaben MÜSSEN überprüft und validiert werden und alles. Wenn Sie nicht wissen, wie Sie 100% sicher sein können, dann tun Sie es nicht.
Grundsätzlich kann ein Benutzer einen beliebigen Code für die betreffende Sprache eingeben und dieser wird ausgeführt. Sie können sich vorstellen, wie viel Schaden er anrichten kann.
quelle
"Wann soll ich verwenden
eval
?" könnte eine bessere Frage sein.Die kurze Antwort lautet "Wenn Ihr Programm zur Laufzeit ein anderes Programm schreiben und dann ausführen soll". Genetische Programmierung ist ein Beispiel für eine Situation, in der die Verwendung wahrscheinlich sinnvoll ist
eval
.quelle
IMO, diese Frage ist nicht spezifisch für LISP . Hier ist eine Antwort auf dieselbe Frage für PHP, die für LISP, Ruby und andere Sprachen gilt, die eine Bewertung haben:
Von hier genommen .
Ich denke, das Trickyness-Stück ist ein erstaunlicher Punkt. Die Besessenheit von Code Golf und prägnantem Code hat immer zu "cleverem" Code geführt (für den Auswertungen ein großartiges Werkzeug sind). Aber Sie sollten Ihren Code zur besseren Lesbarkeit schreiben, IMO, um nicht zu demonstrieren, dass Sie ein Schlauer sind, und um kein Papier zu sparen (Sie werden es sowieso nicht drucken).
Dann gibt es in LISP ein Problem im Zusammenhang mit dem Kontext, in dem eval ausgeführt wird, sodass nicht vertrauenswürdiger Code Zugriff auf weitere Dinge erhalten kann. Dieses Problem scheint sowieso häufig zu sein.
quelle
Es gab viele gute Antworten, aber hier ist eine weitere Einstellung von Matthew Flatt, einem der Implementierer von Racket:
http://blog.racket-lang.org/2011/10/on-eval-in-dynamic-languages-generally.html
Er macht viele der Punkte, die bereits behandelt wurden, aber einige Leute mögen seine Meinung trotzdem interessant finden.
Zusammenfassung: Der Kontext, in dem es verwendet wird, wirkt sich auf das Ergebnis der Bewertung aus, wird jedoch von Programmierern häufig nicht berücksichtigt, was zu unerwarteten Ergebnissen führt.
quelle
Die kanonische Antwort ist, fern zu bleiben. Was ich seltsam finde, weil es ein Primitiv ist, und von den sieben Primitiven (die anderen sind Nachteile, Auto, CDR, If, Gl. Und Zitat) wird es mit Abstand am wenigsten benutzt und geliebt.
Von On Lisp : "Normalerweise ist das explizite Anrufen von eval wie der Kauf von etwas in einem Geschenkeladen am Flughafen. Nachdem Sie bis zum letzten Moment gewartet haben, müssen Sie für eine begrenzte Auswahl zweitklassiger Waren hohe Preise zahlen."
Wann verwende ich eval? Eine normale Verwendung besteht darin, eine REPL in Ihrer REPL durch Auswertung zu haben
(loop (print (eval (read))))
. Jeder ist damit einverstanden.Sie können Funktionen aber auch in Form von Makros definieren, die nach der Kompilierung ausgewertet werden, indem Sie eval mit backquote kombinieren. Du gehst
und es wird den Kontext für Sie töten.
Swank (für Emacs Slime) ist voll von diesen Fällen. Sie sehen so aus:
Ich denke nicht, dass es ein schmutziger Hack ist. Ich benutze es die ganze Zeit selbst, um Makros wieder in Funktionen zu integrieren.
quelle
Noch ein paar Punkte zu Lisp eval:
quelle
Wie die GOTO "Regel": Wenn Sie nicht wissen, was Sie tun, können Sie ein Chaos machen.
Abgesehen davon, dass nur etwas aus bekannten und sicheren Daten erstellt wird, gibt es das Problem, dass einige Sprachen / Implementierungen den Code nicht genug optimieren können. Sie könnten mit interpretiertem Code im Inneren enden
eval
.quelle
Eval ist einfach unsicher. Zum Beispiel haben Sie folgenden Code:
Jetzt kommt der Benutzer zu Ihrer Website und gibt die URL http://example.com/file.php?user= ein ) ein. $ Is_admin = true; echo (
Dann wäre der resultierende Code:
quelle
eval
in jeder Sprache, die es hat.Eval ist nicht böse. Eval ist nicht kompliziert. Es ist eine Funktion, die die Liste zusammenstellt, die Sie an sie übergeben. In den meisten anderen Sprachen würde das Kompilieren von beliebigem Code bedeuten, den AST der Sprache zu lernen und in den Compiler-Interna herumzuwühlen, um die Compiler-API herauszufinden. In lisp nennt man einfach eval.
Wann sollten Sie es verwenden? Wann immer Sie etwas kompilieren müssen, normalerweise ein Programm, das zur Laufzeit beliebigen Code akzeptiert, generiert oder ändert .
Wann solltest du es nicht benutzen? Alle anderen Fälle.
Warum sollten Sie es nicht verwenden, wenn Sie es nicht brauchen? Weil Sie etwas auf unnötig komplizierte Weise tun würden, was zu Problemen bei der Lesbarkeit, Leistung und beim Debuggen führen könnte.
Ja, aber wenn ich ein Anfänger bin, woher weiß ich, ob ich es verwenden soll? Versuchen Sie immer, das, was Sie brauchen, mit Funktionen zu implementieren. Wenn das nicht funktioniert, fügen Sie Makros hinzu. Wenn das immer noch nicht funktioniert, dann bewerten!
Befolgen Sie diese Regeln und Sie werden niemals Böses mit eval tun :)
quelle
Ich mag Zaks Antwort sehr und er hat den Kern der Sache verstanden: eval wird verwendet, wenn Sie eine neue Sprache, ein Skript oder eine Änderung einer Sprache schreiben. Er erklärt nicht weiter, also werde ich ein Beispiel geben:
In diesem einfachen Lisp-Programm wird der Benutzer zur Eingabe aufgefordert, und alles, was er eingibt, wird ausgewertet. Damit dies funktioniert, muss der gesamte Satz von Symboldefinitionen vorhanden sein, wenn das Programm kompiliert wird, da Sie keine Ahnung haben, welche Funktionen der Benutzer eingeben könnte, und Sie alle einschließen müssen. Das heißt, wenn Sie dieses einfache Programm kompilieren, ist die resultierende Binärdatei gigantisch.
Aus diesem Grund können Sie dies grundsätzlich nicht einmal als kompilierbare Aussage betrachten. Sobald Sie eval verwenden , arbeiten Sie im Allgemeinen in einer interpretierten Umgebung, und der Code kann nicht mehr kompiliert werden. Wenn Sie eval nicht verwenden , können Sie ein Lisp- oder Scheme-Programm wie ein C-Programm kompilieren. Daher möchten Sie sicherstellen, dass Sie sich in einer interpretierten Umgebung befinden möchten und müssen, bevor Sie sich zur Verwendung von eval verpflichten .
quelle