Есть несколько причин, по которым нельзя использовать EVAL
.
Основная причина для новичков: вам это не нужно.
Пример (при условии Common 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
, оценив переменную:
(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
: он часто используется для уродливых взломов.