Wiem, że programiści Lisp i Scheme zwykle mówią, że eval
należy tego unikać, chyba że jest to absolutnie konieczne. Widziałem to samo zalecenie dla kilku języków programowania, ale nie widziałem jeszcze listy jasnych argumentów przeciwko używaniu eval
. Gdzie mogę znaleźć opis potencjalnych problemów z używaniem eval
?
Na przykład znam problemy związane z GOTO
programowaniem proceduralnym (sprawia, że programy są nieczytelne i trudne w utrzymaniu, utrudniają znalezienie problemów z bezpieczeństwem itp.), Ale nigdy nie widziałem argumentów przeciwko eval
.
Co ciekawe, te same argumenty przeciwko GOTO
kontynuacjom powinny być ważne, ale widzę, że na przykład Schemerzy nie powiedzą, że kontynuacje są „złe” - należy po prostu zachować ostrożność podczas ich używania. Są znacznie bardziej skłonni do marszczenia się na używaniu kodu eval
niż na kod używający kontynuacji (o ile widzę - mogę się mylić).
Odpowiedzi:
Istnieje kilka powodów, dla których nie powinno się ich używać
EVAL
.Głównym powodem dla początkujących jest: nie potrzebujesz tego.
Przykład (zakładając Common Lisp):
OCENA wyrażenia za pomocą różnych operatorów:
To lepiej napisać jako:
Istnieje wiele przykładów, w których początkujący uczący się Lispa myślą, że potrzebują
EVAL
, ale nie potrzebują tego - ponieważ wyrażenia są oceniane i można również ocenić część funkcji. W większości przypadków użycieEVAL
znaku wskazuje na brak zrozumienia ewaluatora.Ten sam problem dotyczy makr. Często początkujący piszą makra, w których powinni pisać funkcje - nie rozumiejąc, do czego naprawdę służą makra i nie rozumiejąc, że funkcja już wykonuje swoje zadanie.
Często jest to niewłaściwe narzędzie do pracy
EVAL
i często wskazuje, że początkujący nie rozumie zwykłych zasad oceny Lisp.Jeśli uważasz, że potrzebujesz
EVAL
, sprawdźFUNCALL
,REDUCE
czyAPPLY
zamiast tego można użyć czegoś takiego lub .FUNCALL
- wywołaj funkcję z argumentami:(funcall '+ 1 2 3)
REDUCE
- wywołaj funkcję na liście wartości i połącz wyniki:(reduce '+ '(1 2 3))
APPLY
- wywołać funkcję z listy jako argumentów:(apply '+ '(1 2 3))
.P: Czy naprawdę potrzebuję eval, czy też kompilator / oceniający już to, czego naprawdę chcę?
Główne powody, których należy unikać w
EVAL
przypadku nieco bardziej zaawansowanych użytkowników:chcesz mieć pewność, że Twój kod jest skompilowany, ponieważ kompilator może sprawdzić kod pod kątem wielu problemów i generuje szybszy kod, czasami DUŻO DUŻO (to współczynnik 1000 ;-)) szybszy kod
kod, który jest skonstruowany i musi zostać oceniony, nie może zostać skompilowany tak wcześnie, jak to możliwe.
ocena arbitralnych danych wejściowych użytkownika stwarza problemy związane z bezpieczeństwem
niektóre zastosowania ewaluacji
EVAL
mogą się wydarzyć w złym czasie i spowodować problemy z kompilacjąAby wyjaśnić ostatni punkt za pomocą uproszczonego przykładu:
Więc może zechcę napisać makro, które na podstawie pierwszego parametru używa albo
SIN
lubCOS
.(foo 3 4)
robi(sin 4)
i(foo 1 4)
robi(cos 4)
.Teraz możemy mieć:
Nie daje to pożądanego rezultatu.
Wtedy można chcieć naprawić makro
FOO
, oceniając zmienną:Ale to nadal nie działa:
Wartość zmiennej po prostu nie jest znana w czasie kompilacji.
Ogólny ważny powód, którego należy unikać
EVAL
: jest często używany do brzydkich hacków.źródło
eval
prostu dlatego, że nie wiedzą, że istnieje konkretny język lub funkcja biblioteki, aby robić to, co chcą. Podobny przykład z JS: Chcę uzyskać właściwość z obiektu za pomocą dynamicznej nazwy, więc piszę:eval("obj.+" + propName)
kiedy mogłem napisaćobj[propName]
."obj.+"
? Ostatnio sprawdzałem,+
nie działa, gdy używam odwołań kropkowych w JS.eval
(w każdym języku) nie jest zła w taki sam sposób, w jaki piła łańcuchowa nie jest zła. To jest narzędzie. Okazuje się, że jest to potężne narzędzie, które w przypadku niewłaściwego użycia może odciąć kończyny i wypatroszyć (mówiąc metaforycznie), ale to samo można powiedzieć o wielu narzędziach w zestawie narzędzi programisty, w tym:goto
i przyjacieleJeśli zauważysz, że musisz użyć któregokolwiek z tych potężnych, potencjalnie niebezpiecznych narzędzi, trzy razy zadaj sobie pytanie „dlaczego?” w łańcuchu. Na przykład:
Jeśli dojdziesz do końca tego łańcucha, a narzędzie nadal wygląda tak, jakby było to właściwe, zrób to. Udokumentuj z tego piekło. Wypróbujcie z tego piekło. Wielokrotnie sprawdzaj poprawność i bezpieczeństwo. Ale zrób to.
źródło
Eval jest w porządku, o ile dokładnie wiesz, co się z nim dzieje. Każde wprowadzane przez użytkownika dane wejściowe MUSZĄ zostać sprawdzone i zatwierdzone i wszystko. Jeśli nie wiesz, jak być w 100% pewnym, nie rób tego.
Zasadniczo użytkownik może wpisać dowolny kod dla danego języka i zostanie on wykonany. Możesz sobie wyobrazić, ile szkód może wyrządzić.
źródło
„Kiedy używać
eval
?” mogłoby być lepszym pytaniem.Krótka odpowiedź brzmi: „kiedy twój program ma napisać inny program w czasie wykonywania, a następnie go uruchomić”. Programowanie genetyczne jest przykładem sytuacji, w której prawdopodobnie ma sens
eval
.źródło
IMO, to pytanie nie jest specyficzne dla LISP . Oto odpowiedź na to samo pytanie dla PHP i dotyczy to LISP, Ruby i innych języków, które mają wartość eval:
Zaczerpnięte stąd .
Myślę, że ten podstępny kawałek jest niesamowity. Obsesja na punkcie kodu golfowego i zwięzłego kodu zawsze skutkowała „sprytnym” kodem (dla którego evals są świetnym narzędziem). Ale powinieneś napisać swój kod pod kątem czytelności, IMO, nie po to, aby pokazać, że jesteś głupcem i nie oszczędzać papieru (i tak go nie będziesz drukować).
Następnie w LISP-ie występuje problem związany z kontekstem, w którym uruchamiany jest eval, więc niezaufany kod może uzyskać dostęp do większej liczby rzeczy; i tak wydaje się, że ten problem jest powszechny.
źródło
Było wiele świetnych odpowiedzi, ale oto kolejne spojrzenie Matthew Flatta, jednego z twórców Racketa:
http://blog.racket-lang.org/2011/10/on-eval-in-dynamic-languages-generally.html
Przedstawia wiele kwestii, które zostały już omówione, ale niektórzy ludzie mogą mimo to uznać jego podejście za interesujące.
Podsumowanie: Kontekst, w którym jest używany, wpływa na wynik eval, ale często nie jest brany pod uwagę przez programistów, co prowadzi do nieoczekiwanych wyników.
źródło
Kanoniczna odpowiedź brzmi: trzymaj się z daleka. Co wydaje mi się dziwne, ponieważ jest prymitywne, a spośród siedmiu prymitywów (pozostałe to minusy, samochód, cdr, if, eq i cytat), jest ono bardzo użyteczne i najmniej miłe.
From On Lisp : „Zwykle wyraźne dzwonienie do eval jest jak kupowanie czegoś w sklepie z pamiątkami na lotnisku. Czekając do ostatniej chwili, trzeba zapłacić wysokie ceny za ograniczony wybór towarów drugorzędnych”.
Kiedy więc używać eval? Jednym z normalnych zastosowań jest posiadanie REPL w REPL poprzez ocenę
(loop (print (eval (read))))
. Każdy jest w porządku z tym użyciem.Ale możesz także zdefiniować funkcje w postaci makr, które będą oceniane po kompilacji, łącząc eval z odwrotnym cudzysłowem. Ty idź
i zabije dla ciebie kontekst.
Swank (dla szlamu emacsa) jest pełen takich przypadków. Wyglądają tak:
Nie sądzę, że to brudny hack. Sam używam go cały czas do ponownego zintegrowania makr w funkcje.
źródło
Kolejne punkty dotyczące ewaluacji Lispa:
źródło
Tak jak „zasada” GOTO: jeśli nie wiesz, co robisz, możesz narobić bałaganu.
Oprócz budowania czegoś ze znanych i bezpiecznych danych istnieje problem polegający na tym, że niektóre języki / implementacje nie mogą wystarczająco zoptymalizować kodu. Możesz skończyć z zinterpretowanym kodem w środku
eval
.źródło
Eval jest po prostu niepewny. Na przykład masz następujący kod:
Teraz użytkownik przychodzi do Twojej witryny i wpisuje adres URL http://example.com/file.php?user= ); $ is_admin = true; echo (
Wtedy wynikowy kod wyglądałby tak:
źródło
eval
każdym języku, który go posiada.Eval nie jest zły. Eval nie jest skomplikowany. Jest to funkcja, która kompiluje listę, którą do niej przekazujesz. W większości innych języków kompilacja dowolnego kodu oznaczałaby naukę języka AST i przeszukiwanie wewnętrznych elementów kompilatora, aby znaleźć jego API. Podczas seplenienia po prostu dzwonisz do eval.
Kiedy należy go używać? Ilekroć musisz coś skompilować, zazwyczaj jest to program, który akceptuje, generuje lub modyfikuje dowolny kod w czasie wykonywania .
Kiedy nie należy go używać? Wszystkie inne przypadki.
Dlaczego nie powinieneś go używać, kiedy nie musisz? Ponieważ robiłbyś coś w niepotrzebnie skomplikowany sposób, który może powodować problemy z czytelnością, wydajnością i debugowaniem.
Tak, ale jeśli jestem początkującym, skąd mam wiedzieć, czy powinienem go używać? Zawsze staraj się zaimplementować to, czego potrzebujesz, za pomocą funkcji. Jeśli to nie zadziała, dodaj makra. Jeśli to nadal nie zadziała, eval!
Przestrzegaj tych zasad, a z eval nigdy nie zrobisz zła :)
źródło
Bardzo podoba mi się odpowiedź Zaka i on dotarł do istoty sprawy: eval jest używany, gdy piszesz nowy język, skrypt lub modyfikujesz język. Nie wyjaśnia dalej, więc podam przykład:
W tym prostym programie Lisp użytkownik jest proszony o wprowadzenie danych, a następnie oceniane jest wszystko, co wprowadzi. Aby to zadziałało, cały zestaw definicji symboli musi być obecny, jeśli program jest kompilowany, ponieważ nie masz pojęcia, które funkcje użytkownik może wprowadzić, więc musisz uwzględnić je wszystkie. Oznacza to, że jeśli skompilujesz ten prosty program, wynikowy plik binarny będzie gigantyczny.
Z tego powodu nie można nawet uznać tego oświadczenia za kompilowalny. Ogólnie rzecz biorąc, gdy używasz eval , pracujesz w zinterpretowanym środowisku, a kodu nie można już kompilować. Jeśli nie używasz eval , możesz skompilować program Lisp lub Scheme, tak jak program C. Dlatego chcesz się upewnić, że chcesz i musisz być w interpretowanym środowisku, zanim zdecydujesz się na użycie eval .
źródło