Є кілька причин, чому не слід користуватися EVAL
.
Основна причина для початківців: вам це не потрібно.
Приклад (якщо припустити звичайний Lisp):
ОЦІНЮЙТЕ вираз з різними операторами:
(let ((ops '(+ *)))
(dolist (op ops)
(print (eval (list op 1 2 3)))))
Це краще писати як:
(let ((ops '(+ *)))
(dolist (op ops)
(print (funcall op 1 2 3))))
Є чимало прикладів, коли початківці, які навчаються Ліспу, думають, що їм це потрібно EVAL
, але їм це не потрібно - оскільки вирази оцінюються і можна також оцінити функціональну частину. Більшість випадків використання EVAL
показує недостатнє розуміння оцінювача.
Така ж проблема і з макросами. Часто початківці пишуть макроси, куди вони повинні писати функції - не розуміючи, для чого насправді є макроси, і не розуміючи, що функція вже виконує цю роботу.
Часто це неправильний інструмент для роботи, який використовується, EVAL
і часто вказує на те, що новачок не розуміє звичних правил оцінювання Lisp.
Якщо ви вважаєте, що вам потрібно EVAL
, то перевірте FUNCALL
, REDUCE
чи APPLY
може щось замість цього використовуватись.
FUNCALL
- виклик функції з аргументами: (funcall '+ 1 2 3)
REDUCE
- викликати функцію у списку значень та об'єднати результати: (reduce '+ '(1 2 3))
APPLY
- виклик функції зі списком в якості аргументів: (apply '+ '(1 2 3))
.
Питання: чи мені справді потрібен eval чи компілятор / оцінювач вже те, що мені дійсно хочеться?
Основні причини, яких слід уникати EVAL
для трохи просунутіших користувачів:
ви хочете переконатися, що ваш код скомпільований, тому що компілятор може перевірити код на наявність багатьох проблем і генерувати швидший код, іноді МНОГО СТОЛЬКО (це фактор 1000 ;-)) швидший код
Код, який побудований і який повинен бути оцінений, не може бути скомпільований якомога раніше.
eval довільного введення користувача відкриває проблеми із безпекою
деяке використання оцінювання з EVAL
може трапитися в неправильний час і створити проблеми зі складанням
Пояснити останній пункт на спрощеному прикладі:
(defmacro foo (a b)
(list (if (eql a 3) 'sin 'cos) b))
Отже, я можу захотіти написати макрос, який на основі першого параметра використовує або SIN
або COS
.
(foo 3 4)
робить (sin 4)
і (foo 1 4)
робить (cos 4)
.
Тепер ми можемо мати:
(foo (+ 2 1) 4)
Це не дає бажаного результату.
Тоді ви можете відновити макрос FOO
шляхом EVALuating змінної:
(defmacro foo (a b)
(list (if (eql (eval a) 3) 'sin 'cos) b))
(foo (+ 2 1) 4)
Але тоді це все ще не працює:
(defun bar (a b)
(foo a b))
Значення змінної просто невідомо під час компіляції.
Загальна важлива причина, якої слід уникати EVAL
: її часто використовують для некрасивих хак.